Escritura gradual: "Casi todos los idiomas con un sistema de tipos estático también tienen un sistema de tipos dinámico"

20

Este reclamo de Aleks Bromfield afirma:

Casi todos los idiomas con un sistema de tipo estático también tienen un sistema de tipo dinámico. Aparte de C, no puedo pensar en una excepción

¿Es este un reclamo válido? Entiendo que con las clases Reflection o Loading en tiempo de ejecución Java se vuelve un poco así, pero ¿puede esta idea de 'tipeo gradual' extenderse a una gran cantidad de idiomas?

ojo de halcón
fuente
No soy un experto en idiomas, pero algunos que me vienen a la mente al instante que no creo que tengan tipos dinámicos (de forma nativa, sin decir que no se puede improvisar algo en estos) - Fortran, Ada, Pascal, Cobol
mattnz
44
Soy un experto en idiomas y no estoy seguro de lo que dice este tipo. "static" y "dynamic" son nombres incorrectos y generalmente indican el tiempo de verificación / análisis de tipo (ya sea en tiempo de compilación o en tiempo de ejecución). Algunos lenguajes concilian ambos (por ejemplo, al no permitir operaciones en tipos no admitidos durante la compilación y generar excepciones como ClassCastException durante el tiempo de ejecución), y eso puede ser lo que quiere decir. Si es así, es cierto que C generalmente no realiza la verificación de tipo durante el tiempo de ejecución. Diciendo un lang. no tiene "sistema de tipo dinámico" no tiene sentido.
Thiago Silva

Respuestas:

37

Tweeter original aquí. :)

En primer lugar, ¡estoy algo divertido / sorprendido de que mi tweet se tome tan en serio! Si hubiera sabido que iba a ser tan ampliamente difundido, ¡habría pasado más de 30 segundos escribiéndolo!

Thiago Silva tiene razón al señalar que "estático" y "dinámico" describen con mayor precisión la verificación de tipos , en lugar de los sistemas de tipos . De hecho, tampoco es realmente exacto decir que un idioma está tipeado estática o dinámicamente. Por el contrario, un lenguaje tiene un sistema de tipos, y una implementación de ese lenguaje podría imponer el sistema de tipos mediante la verificación estática, o la verificación dinámica, o ambas, o ninguna (¡aunque eso no sería una implementación de lenguaje muy atractiva!).

Como sucede, hay ciertos sistemas de tipos (o características de los sistemas de tipos) que son más susceptibles a la verificación estática, y hay ciertos sistemas de tipos (o características de los sistemas de tipos) que son más susceptibles a la verificación dinámica. Por ejemplo, si su idioma le permite especificar en el texto de un programa que un valor particular siempre debe ser una matriz de enteros, entonces es razonablemente sencillo escribir un verificador estático para verificar esa propiedad. Por el contrario, si su idioma tiene subtipos, y si permite el downcasting, entonces es razonablemente sencillo verificar la validez de un downcast en tiempo de ejecución, pero es extremadamente difícil hacerlo en tiempo de compilación.

Lo que realmente quise decir con mi tweet es simplemente que la gran mayoría de las implementaciones de lenguaje realizan una cierta cantidad de verificación de tipo dinámico. O, de manera equivalente, la gran mayoría de los idiomas tienen algunas características que son difíciles (si no imposibles) de verificar estáticamente. Downcasting es un ejemplo. Otros ejemplos incluyen desbordamiento aritmético, verificación de límites de matriz y verificación nula. Algunos de estos pueden verificarse estáticamente en algunas circunstancias, pero en general, sería difícil encontrar una implementación de lenguaje que no haga ninguna verificación en tiempo de ejecución.

Esto no es algo malo. Es solo una observación de que hay muchas propiedades interesantes que nos gustaría que nuestros idiomas aplicaran, y que realmente no sabemos cómo verificar estáticamente. Y es un recordatorio de que las distinciones como "tipos estáticos" versus "tipos dinámicos" no son tan claras como algunas personas quisieran hacer creer. :)

Una nota final: los términos "fuerte" y "débil" no se usan realmente en la comunidad de investigación del lenguaje de programación, y realmente no tienen un significado consistente. En general, descubrí que cuando alguien dice que un idioma tiene "mecanografía fuerte" y otro idioma tiene "mecanografía débil", realmente está diciendo que su idioma favorito (el que tiene "mecanografía fuerte") les impide cometiendo un error que el otro idioma (el que tiene "mecanografía débil") no lo hace, o por el contrario, que su idioma favorito (el que tiene "mecanografía débil") les permite hacer algo interesante que el otro idioma (el uno con "mecanografía fuerte") no.

Aleks Bromfield
fuente
99
"Es solo una observación de que hay muchas propiedades interesantes que nos gustaría que nuestros idiomas aplicaran, y que realmente no sabemos cómo verificar estáticamente". - Bueno, no es solo que no sabemos cómo hacerlo. "¿Tiene este programa detenerse para esta entrada" es una propiedad interesante que no sólo no sabemos cómo comprobar, pero en realidad nos hacen saber es imposible de comprobar.
Jörg W Mittag
"Otros ejemplos incluyen desbordamiento aritmético, verificación de límites de matriz y verificación nula". Solo una nota de que si bien hay algunos idiomas que verifican estas propiedades en el sistema de tipos, incluso en la mayoría de los idiomas escritos dinámicamente, generalmente son una característica de valores , no de tipos. (Sin embargo, la comprobación "nula" es una característica común de los sistemas de tipo estático.)
porglezomp
No es un tipo dinámico del sistema , y un tipo estático del sistema . Incluso si ninguno se aplica, todavía están allí, describiendo qué es correcto y qué es un error. El lenguaje / implementación puede o no verificar ninguno de ellos. Por cierto, el sistema de tipo estático y dinámico debería ser consistente, pero no es el caso por definición .
Elazar
Cada idioma tiene un sistema de tipo dinámico, que es un conjunto de reglas que prohíben ciertas operaciones.
Elazar
7

Bueno, sí. Puede tener bolsas de propiedades en cualquier idioma estáticamente escrito. La sintaxis será terrible, mientras que al mismo tiempo obtendrá todas las desventajas del sistema de tipo dinámico. Por lo tanto, no hay realmente ninguna ventaja a menos que el compilador le permita usar una sintaxis más agradable, algo como lo dynamicestá haciendo C # con .

Además, puedes hacer eso fácilmente en C también.

En reacción a otras respuestas: creo que la gente está confundiendo la escritura estática / dinámica con la escritura fuerte / débil. La escritura dinámica se trata de poder cambiar la estructura de los datos en tiempo de ejecución y el código puede utilizar datos que se ajusten a lo que el código necesita. Esto se llama Duck Typing .

Mencionar la reflexión no es contar toda la historia, porque la reflexión no le permite cambiar la estructura de datos existente. No puede agregar un nuevo campo a una clase o estructura en C, C ++, Java o C #. En lenguajes dinámicos, agregar nuevos campos o atributos a las clases existentes no solo es posible, sino que es bastante común.

Por ejemplo, mire Cython , el compilador Python-to-C. Crea código C estático, pero el sistema de tipos aún conserva su naturaleza dinámica. C es un lenguaje estáticamente escrito, pero puede admitir la escritura dinámica desde Python.

Eufórico
fuente
Técnicamente, puede hacerlo en C # a partir de la versión 4 ExpandoObject, aunque es en gran medida un proceso de suscripción, muy diferente a JavaScript o Ruby. Aún así, ha hecho un punto muy importante, que es que la escritura de pato (lo que el 99% de los desarrolladores realmente quieren decir cuando dicen "escrito dinámicamente") y la reflexión no son las mismas cosas.
Aaronaught
También podría agregar que Python no tiene una escritura de pato real. Simplemente tiene algunos ganchos para que "implemente los suyos" (tiene un método mágico que puede regresar Truepara decir "este objeto loco es una instancia de la clase que estoy definiendo"). OCaml tiene esta característica por lo que yo entiendo, pero realmente no lo sé.
vlad-ardelean
6

Los lenguajes dinámicos son lenguajes estáticos . Lo que comúnmente se llama "escritura dinámica" es realmente un caso especial de escritura estática, el caso en el que se ha limitado a tener solo un tipo. Como experimento mental, imagine escribir un programa en Java o C # usando solo Objectvariables / campos / parámetros y descargando inmediatamente antes de llamar a cualquier método. Sería más preciso llamar a lenguajes como Python o Javascript "unitped". (Esta afirmación probablemente confundirá / molestará a muchas personas, teniendo en cuenta que dicho programa Java o C # usaría muchos subtipos, pero eso se debe a que el lenguaje OOP promedio combina tipos y clases. Lea la publicación del blog para obtener más detalles).

Tenga en cuenta que incluso C tiene una escritura "dinámica": puede lanzar cualquier puntero a un puntero void(y si la memoria me sirve char) y viceversa. Y tenga en cuenta, también, que no hay tiempo de ejecución comprobando allí; Si te equivocas, ¡disfruta de tu comportamiento indefinido!

Doval
fuente
Pero el reparto se hace estáticamente. No veo cómo este es un ejemplo de tipeo dinámico.
osa
@osa Solo porque el código dice String foo = (String) barque no significa que barde hecho es a String. Solo puede saberlo con seguridad en el tiempo de ejecución, por lo que no veo cómo se realiza el reparto "estáticamente".
Doval
No estoy de acuerdo con esto Al menos en Javascript, puede agregar nuevos métodos y propiedades a los objetos en tiempo de ejecución. En C #, debe usar explícitamente un dynamicobjeto para hacer esto. Si intenta agregar una propiedad a un object... bueno, no puede.
Arturo Torres Sánchez
3

La diferencia entre la tipificación estática y dinámica es cuando se marca el tipo de un valor: tiempo de compilación versus tiempo de ejecución. En los lenguajes donde los valores llevan su tipo con ellos (por ejemplo, objetos Java), siempre puede recurrir a la escritura dinámica, incluso cuando el idioma realmente prefiere la escritura estática. Aquí hay un ejemplo en Java, con un método de tipo dinámico:

void horribleJava(List untypedList) {
  for (Object o : untypedList)
    ((SpecificType) o).frobnicate();
}

Observe cómo se verifica el tipo de cada elemento en tiempo de ejecución. El método equivalente de tipo estático es:

void goodJava(List<SpecificType> typedList) {
  for (SpecificType o : typedList) {
    o.forbnicate();
  }
}

En C, los valores (y específicamente los punteros) no retienen su tipo durante el tiempo de ejecución; cada puntero es equivalente a a void *. En cambio, las variables y las expresiones tienen un tipo. Para lograr una escritura dinámica, debe llevar la información de tipo usted mismo (por ejemplo, como un campo en una estructura).

amon
fuente
1
No creo que un elenco cuente como tipeo dinámico en ningún sentido realmente práctico. Todavía requiere conocimiento del tipo en tiempo de compilación, solo difiere la validación real hasta el tiempo de ejecución. No puede invocar el frobnicatemétodo aquí sin antes saberlo SpecificType.
Aaronaught
2
@Aaronaught Pero esto es tipeo dinámico una vez que hemos diferido la verificación de tipo al tiempo de ejecución. Tenga en cuenta que mis dos ejemplos usan una escritura nominativa que requiere un nombre de tipo específico. Está pensando en la tipificación estructural , por ejemplo, la tipificación de pato que requiere la presencia de algún método. Los ejes estructural vs. nominativo y estático vs. dinámico son ortogonales entre sí (Java es nominativo y mayormente estático; ML y Go son estructurales y estáticos; Perl, Python y Ruby son (principalmente) estructurales y dinámicos).
amon
Como dije en mi último comentario, es una distinción teórica que ningún programador que he conocido realmente se preocupa. Se puede argumentar de manera abrasiva que las personas están usando la terminología incorrecta (y de hecho parece que el tweeter original estaba buscando precisamente ese tipo de sarcasmo), pero para las personas que realmente están en las trincheras, tipeo dinámico = tipeo de pato. De hecho, esa definición es tan común que langues como C # realmente la ha codificado (es decir, con la dynamicpalabra clave). Igualar estático a tiempo de compilación y dinámico a tiempo de ejecución en su mayoría simplemente enturbia las aguas.
Aaronaught
@Aaronaught: Si uno lanza a una interfaz, y si las clases que implementan un método con el significado deseado implementan consistentemente la misma interfaz para decirlo, entonces esencialmente se estaría escribiendo en tiempo de ejecución. Si bien algunos podrían argumentar que "escribir pato" debería usar el nombre del método, creo que sería más útil considerar el nombre del método "real" como el nombre de la interfaz más el nombre del método. De lo contrario, debe [1,2].Add([3,4])ceder [1,2,3,4], [1,2,[3,4]]o [4,6]?
supercat
@supercat: Ninguno de los anteriores, debería fallar porque no hay ningún Addmétodo en la matriz que acepte una matriz porque dicho método sería ambiguo. La escritura de pato no te excusa de escribir tipos y funciones comprensibles.
Aaronaught
2

La escritura estática vs. dinámica básicamente se refiere a cómo se verifican los tipos. La tipificación estática significa que la verificación de los tipos de diversas variables o expresiones se verifica en función del código real (generalmente por el compilador) mientras que en un sistema de tipo dinámico esta verificación se realiza solo en tiempo de ejecución, por el entorno de tiempo de ejecución.

Lo que creo que se refiere el texto es que incluso si los tipos se verifican estáticamente, también se verifican en tiempo de ejecución, es decir, dinámicamente. Usted mencionó correctamente Java Reflection; la reflexión solo ocurre en tiempo de ejecución y el Java Runtime Environment (JVM) realmente realiza la verificación de tipos cuando se usa la reflexión, lo que básicamente significa la verificación dinámica de tipos.

m3th0dman
fuente
Entonces no es correcto. En C ++, Pascal, Fortran --- en cualquier lenguaje compilado --- los tipos solo se verifican estáticamente y no dinámicamente (a menos que esté utilizando dynamic_cast). Puede usar punteros para escribir cosas arbitrarias en la memoria, y nadie sabrá los tipos. Entonces, la escritura estática significa SOLO VERIFICAR ESTÁTICAMENTE, mientras que la escritura dinámica significa SOLO VERIFICAR EN TIEMPO DE EJECUCIÓN.
osa
-1

La excitación es incorrecta: C también tiene un importante sistema de tipo dinámico. Simplemente no lo marca ("C está fuertemente tipado, débilmente marcado"). Por ejemplo, tratar una estructura como un double( reinternpret_castestilo) genera un comportamiento indefinido, un error de tipo dinámico.

Elazar
fuente
(A quien haya votado en contra - el comentario de @Thiago Silva dice lo mismo)
Elazar