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.