¿Qué funcionalidad permite la escritura dinámica? [cerrado]

91

He estado usando Python durante unos días y creo que entiendo la diferencia entre la escritura dinámica y la estática. Lo que no entiendo es bajo qué circunstancias sería preferible. Es flexible y legible, pero a expensas de más controles de tiempo de ejecución y pruebas unitarias adicionales requeridas.

Además de criterios no funcionales como la flexibilidad y la legibilidad, ¿qué razones hay para elegir la escritura dinámica? ¿Qué puedo hacer con la escritura dinámica que no es posible de otra manera? ¿Qué ejemplo de código específico se te ocurre que ilustra una ventaja concreta del tipeo dinámico?

Justin984
fuente
55
Teóricamente, tampoco hay nada que no puedas hacer, siempre y cuando los idiomas estén completos en Turing . La pregunta más interesante para mí es qué es fácil o natural en uno frente al otro. Hay cosas que hago regularmente en Python que ni siquiera consideraría en C ++ aunque sé que es capaz.
Mark Ransom
28
Como Chris Smith escribe en su excelente ensayo Qué saber antes de debatir los sistemas de tipos : "El problema, en este caso, es que la mayoría de los programadores tienen una experiencia limitada y no han probado muchos idiomas. Para el contexto, aquí, seis o siete no cuenta como "mucho" ... Dos consecuencias interesantes de esto son: (1) Muchos programadores han usado lenguajes tipados estáticamente muy pobres. (2) Muchos programadores han usado muy mal los lenguajes tipados dinámicamente ".
Daniel Pryden
3
@suslik: Si las primitivas del lenguaje tienen tipos sin sentido, entonces, por supuesto, puedes hacer cosas sin sentido con los tipos. Eso no tiene nada que ver con la diferencia entre tipeo estático y dinámico.
Jon Purdy
10
@CzarekTomczak: Esa es una característica de algunos lenguajes de tipo dinámico, sí. Pero es posible que un lenguaje de tipo estático se pueda modificar en tiempo de ejecución. Por ejemplo, Visual Studio le permite reescribir el código C # mientras se encuentra en un punto de interrupción en el depurador, e incluso rebobinar el puntero de instrucción para volver a ejecutar su código con nuevos cambios. Como cité a Chris Smith en mi otro comentario: "Muchos programadores han usado lenguajes de escritura estática muy pobres" - no juzguen todos los idiomas de escritura estática por los que usted conoce.
Daniel Pryden
11
@WarrenP: Usted afirma que "los sistemas de tipo dinámico reducen la cantidad de cruft adicional que tengo que escribir", pero luego compara Python con C ++. Esa no es una comparación justa: por supuesto, C ++ es más detallado que Python, pero eso no se debe a la diferencia en sus sistemas de tipos, sino a la diferencia en sus gramáticas. Si solo desea reducir el número de caracteres en la fuente de su programa, aprenda J o APL: le garantizo que serán más cortos. Una comparación más justa sería comparar Python con Haskell. (Para el registro: me encanta Python y lo prefiero a C ++, pero me gusta Haskell aún más).
Daniel Pryden

Respuestas:

50

Como solicitó un ejemplo específico, le daré uno.

El ORM masivo de Rob Conery tiene 400 líneas de código. Es así de pequeño porque Rob puede asignar tablas SQL y proporcionar resultados de objetos sin requerir muchos tipos estáticos para reflejar las tablas SQL. Esto se logra utilizando el dynamictipo de datos en C #. La página web de Rob describe este proceso en detalle, pero parece claro que, en este caso de uso particular, la tipificación dinámica es en gran parte responsable de la brevedad del código.

Compárese con Dapper de Sam Saffron , que usa tipos estáticos; SQLMappersolo la clase tiene 3000 líneas de código.

Tenga en cuenta que se aplican las exenciones de responsabilidad habituales, y su kilometraje puede variar; Dapper tiene objetivos diferentes a los de Massive. Solo señalo esto como un ejemplo de algo que puedes hacer en 400 líneas de código que probablemente no sería posible sin una escritura dinámica.


La escritura dinámica le permite diferir sus decisiones de tipo en tiempo de ejecución. Eso es todo.

Ya sea que use un lenguaje de tipo dinámico o uno de tipo estático, sus elecciones de tipo aún deben ser sensatas. No va a agregar dos cadenas juntas y esperar una respuesta numérica a menos que las cadenas contengan datos numéricos, y si no lo hacen, obtendrá resultados inesperados. Un idioma escrito estáticamente no le permitirá hacer esto en primer lugar.

Los defensores de los lenguajes de tipo estático señalan que el compilador puede hacer una cantidad sustancial de "comprobación de sanidad" de su código en tiempo de compilación, antes de que se ejecute una sola línea. Esta es una buena cosa ™.

C # tiene la dynamicpalabra clave, que le permite diferir la decisión de tipo en tiempo de ejecución sin perder los beneficios de la seguridad de tipo estático en el resto de su código. La inferencia de tipos ( var) elimina gran parte del dolor de escribir en un lenguaje de tipo estático al eliminar la necesidad de declarar siempre tipos explícitamente.


Los lenguajes dinámicos parecen favorecer un enfoque más interactivo e inmediato de la programación. Nadie espera que tenga que escribir una clase y pasar por un ciclo de compilación para escribir un poco de código Lisp y verlo ejecutar. Sin embargo, eso es exactamente lo que se espera que haga en C #.

Robert Harvey
fuente
22
Si agrego dos cadenas numéricas juntas, todavía no esperaría un resultado numérico.
pdr
22
@Robert Estoy de acuerdo con la mayoría de tu respuesta. Sin embargo, tenga en cuenta que hay lenguajes de tipo estático con bucles interactivos de lectura-evaluación-impresión, como Scala y Haskell. Puede ser que C # simplemente no sea un lenguaje particularmente interactivo.
Andres F.
14
Tengo que aprenderme un Haskell.
Robert Harvey
77
@RobertHarvey: Puede que te sorprenda / impresione con F # si aún no lo has probado. Obtiene toda la seguridad de tipo (tiempo de compilación) que normalmente obtiene en un lenguaje .NET, excepto que rara vez tiene que declarar ningún tipo. La inferencia de tipos en F # va más allá de lo que está disponible / funciona en C #. También: similar a lo que Andrés y Daniel están señalando, F # interactive es parte de Visual Studio ...
Steven Evers
8
"No va a agregar dos cadenas juntas y esperar una respuesta numérica a menos que las cadenas contengan datos numéricos, y si no lo hacen, obtendrá resultados inesperados" lo siento, esto no tiene nada que ver con la escritura dinámica o estática , esto es fuerte frente a mecanografía débil .
vartec
26

Frases como "mecanografía estática" y "mecanografía dinámica" se lanzan mucho, y las personas tienden a usar definiciones sutilmente diferentes, así que comencemos aclarando lo que queremos decir.

Considere un lenguaje que tiene tipos estáticos que se verifican en tiempo de compilación. Pero supongamos que un error de tipo genera solo una advertencia no fatal y, en tiempo de ejecución, todo está tipeado. Estos tipos estáticos son solo para conveniencia del programador y no afectan el codegen. Esto ilustra que la escritura estática no impone por sí misma ninguna limitación y no se excluye mutuamente con la escritura dinámica. (Objective-C se parece mucho a esto).

Pero la mayoría de los sistemas de tipo estático no se comportan de esta manera. Hay dos propiedades comunes de los sistemas de tipo estático que pueden imponer limitaciones:

El compilador puede rechazar un programa que contiene un error de tipo estático.

Esto es una limitación porque muchos programas seguros de tipo contienen necesariamente un error de tipo estático.

Por ejemplo, tengo un script de Python que necesita ejecutarse como Python 2 y Python 3. Algunas funciones cambiaron sus tipos de parámetros entre Python 2 y 3, por lo que tengo un código como este:

if sys.version_info[0] == 2:
    wfile.write(txt)
else:
    wfile.write(bytes(txt, 'utf-8'))

Un verificador de tipo estático Python 2 rechazaría el código Python 3 (y viceversa), aunque nunca se ejecutaría. Mi programa de tipo seguro contiene un error de tipo estático.

Como otro ejemplo, considere un programa Mac que quiera ejecutarse en OS X 10.6, pero aproveche las nuevas características en 10.7. Los métodos 10.7 pueden o no existir en tiempo de ejecución, y depende de mí, el programador, detectarlos. Un verificador de tipo estático se ve obligado a rechazar mi programa para garantizar la seguridad de tipo o aceptar el programa, junto con la posibilidad de producir un error de tipo (falta la función) en tiempo de ejecución.

La verificación de tipo estático supone que el entorno de tiempo de ejecución se describe adecuadamente mediante la información de tiempo de compilación. ¡Pero predecir el futuro es peligroso!

Aquí hay una limitación más:

El compilador puede generar código que asume que el tipo de tiempo de ejecución es el tipo estático.

Asumir que los tipos estáticos son "correctos" ofrece muchas oportunidades para la optimización, pero estas optimizaciones pueden ser limitantes. Un buen ejemplo son los objetos proxy, por ejemplo, la comunicación remota. Supongamos que desea tener un objeto proxy local que reenvíe invocaciones de métodos a un objeto real en otro proceso. Sería bueno si el proxy fuera genérico (para que pueda enmascararse como cualquier objeto) y transparente (para que el código existente no necesite saber que está hablando con un proxy). Pero para hacer esto, el compilador no puede generar código que suponga que los tipos estáticos son correctos, por ejemplo, mediante la inclusión de llamadas a métodos estáticos, porque eso fallará si el objeto es realmente un proxy.

Los ejemplos de dicha comunicación remota en acción incluyen NSXPCConnection de ObjC o TransparentProxy de C # (cuya implementación requirió algunas pesimizaciones en el tiempo de ejecución; consulte aquí para una discusión).

Cuando el codegen no depende de los tipos estáticos, y tiene instalaciones como el reenvío de mensajes, puede hacer muchas cosas interesantes con objetos proxy, depuración, etc.

Así que eso es una muestra de algunas de las cosas que puede hacer si no está obligado a satisfacer un verificador de tipo. Las limitaciones no están impuestas por los tipos estáticos, sino por la verificación forzada de tipos estáticos.

pez ridículo
fuente
2
"Un verificador de tipo estático de Python 2 rechazaría el código de Python 3 (y viceversa), aunque nunca se ejecute. Mi programa seguro de tipo contiene un error de tipo estático". Parece que lo que realmente necesita es algún tipo de "estático if", donde el compilador / intérprete ni siquiera ve el código si la condición es falsa.
David Stone
@davidstone que existe en c ++
Milind R
A Python 2 static type checker would reject the Python 3 code (and vice versa), even though it would never be executed. My type safe program contains a static type error. En cualquier lenguaje estático razonable, puede hacerlo con una IFDEFdeclaración de preprocesador de tipo, mientras mantiene la seguridad de tipo en ambos casos.
Mason Wheeler
1
@MasonWheeler, davidstone No, los trucos del preprocesador y static_if son demasiado estáticos. En mi ejemplo, usé Python2 y Python3, pero podría haber sido simplemente AmazingModule2.0 y AmazingModule3.0, donde algunas interfaces cambiaron entre versiones. Lo más pronto que puede saber que la interfaz es en el momento de la importación del módulo, que es necesariamente en tiempo de ejecución (al menos si desea admitir la vinculación dinámica).
ridiculous_fish
18

Las variables de tipo pato son lo primero en lo que todos piensan, pero en la mayoría de los casos puede obtener los mismos beneficios a través de la inferencia de tipo estático.

Pero escribir pato en colecciones creadas dinámicamente es difícil de lograr de otra manera:

>>> d = JSON.parse(foo)
>>> d['bar'][3]
12
>>> d['baz']['qux']
'quux'

Entonces, ¿qué tipo JSON.parsedevuelve? ¿Un diccionario de matrices de enteros o diccionarios de cadenas? No, incluso eso no es lo suficientemente general.

JSON.parsetiene que devolver algún tipo de "valor de variante" que puede ser nulo, bool, flotante, cadena, matriz de cualquiera de estos tipos recursivamente, o diccionario de cadena a cualquiera de estos tipos recursivamente. Las principales fortalezas de la tipificación dinámica provienen de tener tales tipos de variantes.

Hasta ahora, este es un beneficio de los tipos dinámicos , no de los lenguajes de tipo dinámico. Un lenguaje estático decente puede simular cualquier tipo de ese tipo perfectamente. (E incluso los lenguajes "malos" a menudo pueden simularlos al romper la seguridad del tipo bajo el capó y / o requerir una sintaxis de acceso torpe).

La ventaja de los lenguajes de tipo dinámico es que dichos tipos no pueden inferirse mediante sistemas de inferencia de tipos estáticos. Tienes que escribir el tipo explícitamente. Pero en muchos de estos casos, incluida esta vez, el código para describir el tipo es exactamente tan complicado como el código para analizar / construir los objetos sin describir el tipo, por lo que todavía no es necesariamente una ventaja.

abarnert
fuente
21
Su ejemplo de análisis JSON se puede manejar fácilmente de forma estática mediante un tipo de datos algebraicos.
2
OK, mi respuesta no fue lo suficientemente clara; Gracias. Ese JSValue es una definición explícita de un tipo dinámico, exactamente de lo que estaba hablando. Son esos tipos dinámicos los que son útiles, no los idiomas que requieren tipeo dinámico. Sin embargo, sigue siendo relevante que los tipos dinámicos no puedan ser generados automáticamente por ningún sistema de inferencia de tipo real, mientras que la mayoría de los ejemplos comunes de personas son trivialmente inferibles. Espero que la nueva versión lo explique mejor.
abarnert
44
Los tipos de datos algebraicos de @MattFenwick están bastante restringidos a lenguajes funcionales (en la práctica). ¿Qué pasa con lenguajes como Java y C #?
spirc
44
Los ADT existen en C / C ++ como uniones etiquetadas. Esto no es exclusivo de los lenguajes funcionales.
Clark Gaebel
2
@spirc puede emular ADT en un lenguaje OO clásico utilizando múltiples clases que se derivan de una interfaz común, llamadas en tiempo de ejecución a getClass () o GetType () y comprobaciones de igualdad. O puede usar el envío doble, pero creo que vale más en C ++. Por lo tanto, es posible que tenga una interfaz JSObject y clases JSString, JSNumber, JSHash y JSArray. Luego, necesitaría algún código para convertir esta estructura de datos "sin tipo" en una estructura de datos "con tipo de aplicación". Pero probablemente también desee hacer esto en un lenguaje de tipo dinámico.
Daniel Yankowsky
12

Como cada sistema de tipo estático remotamente práctico está muy limitado en comparación con el lenguaje de programación que le concierne, no puede expresar todos los invariantes que el código podría verificar en tiempo de ejecución. Para no eludir las garantías que un sistema de tipos intenta dar, por lo tanto, opta por ser conservador y no permitir los casos de uso que pasarían estas verificaciones, pero no se puede probar (en el sistema de tipos).

Haré un ejemplo. Suponga que implementa un modelo de datos simple para describir objetos de datos, colecciones de ellos, etc., que está tipado estáticamente en el sentido de que, si el modelo dice que el atributo xdel objeto de tipo Foo contiene un número entero, siempre debe contener un número entero. Como se trata de una construcción en tiempo de ejecución, no puede escribirla estáticamente. Supongamos que almacena los datos descritos en archivos YAML. Usted crea un mapa hash (para ser entregado a una biblioteca YAML más tarde), obtiene el xatributo, lo almacena en el mapa, obtiene ese otro atributo que resulta ser una cadena, ... ¿espera un segundo? ¿Cuál es el tipo de the_map[some_key]ahora? Bueno, dispara, sabemos que some_keyes así 'x'y el resultado debe ser un número entero, pero el sistema de tipos ni siquiera puede comenzar a razonar sobre esto.

Algunos sistemas de tipos investigados activamente pueden funcionar para este ejemplo específico, pero estos son extremadamente complicados (tanto para que los escritores de compiladores los implementen como para que el programador razone), especialmente para algo tan "simple" (quiero decir, lo acabo de explicar en uno párrafo).

Por supuesto, la solución de hoy es boxear todo y luego emitir (o tener un montón de métodos anulados, la mayoría de los cuales generan excepciones "no implementadas"). Pero esto no está estáticamente escrito, es un truco alrededor del sistema de tipos para hacer las verificaciones de tipos en tiempo de ejecución.

usuario7043
fuente
Los tipos genéricos no tienen el requisito de boxeo.
Robert Harvey
@RobertHarvey Sí. No estaba hablando con el boxeo en Java C #, estaba hablando de "envolverlo en alguna clase de contenedor cuyo único propósito es representar un valor de T en un subtipo de U". Sin embargo, el polimorfismo paramétrico (lo que llama tipificación genérica) no se aplica a mi ejemplo. Es una abstracción en tiempo de compilación sobre tipos concretos, pero necesitamos un mecanismo de escritura en tiempo de ejecución.
Puede valer la pena señalar que el sistema de tipos de Scala está Turing completo. Por lo tanto, los sistemas de tipos pueden ser menos triviales de lo que imagina.
Andrea
@Andrea I intencionalmente no reduje mi descripción a la integridad de turing. ¿Alguna vez programado en una tarpit turing? ¿O trató de codificar estas cosas en tipos? En algún momento, se vuelve demasiado complicado para ser factible.
@delnan estoy de acuerdo. Solo estaba señalando que los sistemas de tipos pueden hacer cosas bastante complicadas. Tuve la impresión de que su respuesta significaba que el sistema de tipos solo puede hacer una verificación trivial, ¡pero en una segunda lectura no escribió nada como esto!
Andrea
7

No hay nada que pueda hacer con la escritura dinámica que no pueda hacer con la escritura estática, porque puede implementar la escritura dinámica sobre un lenguaje escrito estáticamente.

Un breve ejemplo en Haskell:

data Data = DString String | DInt Int | DDouble Double

-- defining a '+' operator here, with explicit promotion behavior
DString a + DString b = DString (a ++ b)
DString a + DInt b = DString (a ++ show b)
DString a + DDouble b = DString (a ++ show b)
DInt a + DString b = DString (show a ++ b)
DInt a + DInt b = DInt (a + b)
DInt a + DDouble b = DDouble (fromIntegral a + b)
DDouble a + DString b = DString (show a ++ b)
DDouble a + DInt b = DDouble (a + fromIntegral b)
DDouble a + DDouble b = DDouble (a + b)

Con suficientes casos puede implementar cualquier sistema de tipo dinámico dado.

Por el contrario, también puede traducir cualquier programa estáticamente escrito en uno dinámico equivalente. Por supuesto, perdería todas las garantías de corrección en tiempo de compilación que proporciona el lenguaje estáticamente tipado.

Editar: quería mantener esto simple, pero aquí hay más detalles sobre un modelo de objetos

Una función toma una lista de datos como argumentos y realiza cálculos con efectos secundarios en ImplMonad, y devuelve un dato.

type Function = [Data] -> ImplMonad Data

DMember es un valor de miembro o una función.

data DMember = DMemValue Data | DMemFunction Function

Extienda Datapara incluir objetos y funciones. Los objetos son listas de miembros nombrados.

data Data = .... | DObject [(String, DMember)] | DFunction Function

Estos tipos estáticos son suficientes para implementar todos los sistemas de objetos de tipo dinámico con los que estoy familiarizado.

NovaDenizen
fuente
Esto no es lo mismo en absoluto porque no puede agregar nuevos tipos sin revisar la definición de Data.
Jed
55
Está mezclando conceptos de escritura dinámica con escritura débil en su ejemplo. La escritura dinámica se trata de operar en tipos desconocidos, no definir una lista de tipos permitidos y operaciones de sobrecarga entre ellos.
hcalves
2
@Jed Una vez que haya implementado el modelo de objetos, los tipos fundamentales y las operaciones primitivas, no es necesario ningún otro trabajo preliminar. Puede traducir fácil y automáticamente programas en el lenguaje dinámico original a este dialecto.
NovaDenizen
2
@hcalves Como te refieres a la sobrecarga en mi código Haskell, sospecho que no tienes la idea correcta sobre su semántica. Allí he definido un nuevo +operador que combina dos Datavalores en otro Datavalor. Datarepresenta los valores estándar en el sistema de tipo dinámico.
NovaDenizen
1
@Jed: La mayoría de los lenguajes dinámicos tienen un pequeño conjunto de tipos "primitivos" y alguna forma inductiva de introducir nuevos valores (estructuras de datos como listas). El esquema, por ejemplo, llega bastante lejos con poco más que átomos, pares y vectores. Debería poder implementarlos de la misma manera que el resto del tipo dinámico dado.
Tikhon Jelvis
3

Membranas :

Una membrana es una envoltura alrededor de un gráfico de objeto completo, a diferencia de una envoltura para un solo objeto. Por lo general, el creador de una membrana comienza envolviendo un solo objeto en una membrana. La idea clave es que cualquier referencia de objeto que cruza la membrana está envuelta transitivamente en la misma membrana.

ingrese la descripción de la imagen aquí

Cada tipo está envuelto por un tipo que tiene la misma interfaz, pero que intercepta mensajes y envuelve y desenvuelve valores a medida que cruzan la membrana. ¿Cuál es el tipo de la función de ajuste en su idioma estático favorito? Quizás Haskell tiene un tipo para esas funciones, pero la mayoría de los lenguajes tipados estáticamente no lo hacen o terminan usando Object → Object, renunciando efectivamente a su responsabilidad como verificadores de tipo.

Mike Samuel
fuente
44
Sí, Haskell puede hacerlo utilizando tipos existenciales. Si tiene algún tipo de clase Foo, puede hacer un envoltorio alrededor de cualquier tipo de instancia de esa interfaz. class Foo a where ... data Wrapper = forall a. Foo a => Wrapper a
Jake McArthur
@JakeMcArthur, gracias por explicarlo. Esa es otra razón para que me siente y aprenda Haskell.
Mike Samuel
2
Su membrana es una 'interfaz' y los tipos de objetos están "mecanografiados existencialmente", es decir, sabemos que existen debajo de la interfaz, pero eso es todo lo que sabemos. Los tipos existenciales para la abstracción de datos se conocen desde los años 80. Una buena referencia es cs.cmu.edu/~rwh/plbook/book.pdf capítulo 21.1
Don Stewart,
@DonStewart. ¿Son las clases proxy de Java un mecanismo de tipo existencial entonces? Un lugar donde las membranas se vuelven difíciles es en idiomas con sistemas de tipos nominales que tienen nombres para tipos concretos visibles fuera de la definición de ese tipo. Por ejemplo, no se puede ajustar Stringya que es un tipo concreto en Java. Smalltalk no tiene este problema porque no intenta escribir #doesNotUnderstand.
Mike Samuel
1

Como alguien mencionó, en teoría no hay mucho que pueda hacer con la escritura dinámica que no podría hacer con la escritura estática si implementara ciertos mecanismos por su cuenta. La mayoría de los lenguajes proporcionan mecanismos de relajación de tipos para admitir la flexibilidad de tipos, como punteros vacíos y el tipo de objeto raíz o interfaz vacía.

La mejor pregunta es por qué la escritura dinámica es más adecuada y más apropiada en ciertas situaciones y problemas.

Primero, definamos

Entidad : necesitaría una noción general de alguna entidad en el código. Puede ser cualquier cosa, desde un número primitivo hasta datos complejos.

Comportamiento : digamos que nuestra entidad tiene algún estado y un conjunto de métodos que permiten al mundo exterior instruir a la entidad ante ciertas reacciones. Llamemos al estado + interfaz de esta entidad su comportamiento. Una entidad puede tener más de un comportamiento combinado de cierta manera por las herramientas que proporciona el lenguaje.

Definiciones de entidades y sus comportamientos : cada lenguaje proporciona algunos medios de abstracción que lo ayudan a definir comportamientos (conjunto de métodos + estado interno) de ciertas entidades en el programa. Puede asignar un nombre a estos comportamientos y decir que todas las instancias que tienen este comportamiento son de cierto tipo .

Esto es probablemente algo que no es tan desconocido. Y como dijiste, entendiste la diferencia, pero aún así. Probablemente no sea una explicación completa y más precisa, pero espero que sea lo suficientemente divertida como para aportar algo de valor :)

Escritura estática : el comportamiento de todas las entidades en su programa se examina en tiempo de compilación, antes de que el código comience a ejecutarse. Esto significa que si desea, por ejemplo, que su entidad de tipo Persona tenga un comportamiento (para comportarse como) Magician, entonces debería definir la entidad MagicianPerson y darle comportamientos de un mago como throwMagic (). Si está en su código, dígale por error al compilador Person.throwMagic () normal que le dirá"Error >>> hell, this Person has no this behavior, dunno throwing magics, no run!".

Escritura dinámica : en los entornos de escritura dinámica, los comportamientos disponibles de las entidades no se verifican hasta que realmente intente hacer algo con cierta entidad. Ejecutar el código Ruby que le pide a Person.throwMagic () no se detectará hasta que su código realmente llegue allí. Esto suena frustrante, ¿no es así? Pero también suena revelador. Según esta propiedad, puede hacer cosas interesantes. Por ejemplo, digamos que diseñas un juego en el que cualquier cosa puede recurrir a Magician y realmente no sabes quién será, hasta que llegues a cierto punto del código. Y luego viene Frog y tú dicesHeyYouConcreteInstanceOfFrog.include Magicy a partir de entonces esta rana se convierte en una rana particular que tiene poderes mágicos. Otras ranas, todavía no. Verá, en lenguajes de escritura estáticos, tendría que definir esta relación por algún medio estándar de combinación de comportamientos (como la implementación de la interfaz). En el lenguaje de escritura dinámico, puede hacerlo en tiempo de ejecución y a nadie le importará.

La mayoría de los lenguajes de escritura dinámica tienen mecanismos para proporcionar un comportamiento genérico que capturará cualquier mensaje que se pase a su interfaz. Por ejemplo Ruby method_missingy PHP __callsi recuerdo bien. Eso significa que puede hacer cualquier tipo de cosas interesantes en tiempo de ejecución del programa y tomar una decisión de tipo basada en el estado actual del programa. Esto trae herramientas para modelar un problema que son mucho más flexibles que, por ejemplo, en un lenguaje de programación estático conservador como Java.

ivanjovanovic
fuente