En la teoría de los lenguajes de programación , el tipado sensible al flujo (también llamado tipado de flujo o tipado de ocurrencia ) es un sistema de tipos donde el tipo de una expresión depende de su posición en el flujo de control .
En los lenguajes de tipado estático , el tipo de una expresión viene determinado por los tipos de las subexpresiones que la componen. Sin embargo, en el tipado sensible al flujo, el tipo de una expresión puede actualizarse a un tipo más específico si sigue una operación que valida su tipo. Las operaciones de validación pueden incluir predicados de tipo, actualizaciones imperativas y control de flujo.
Ejemplos
Ceilán
Este ejemplo en Ceilán ilustra el concepto:
// Object? significa que la variable "name" es de tipo Object o null void hello ( Object ? name ) { if ( is String name ) { // "name" ahora tiene tipo String en este bloque print ( "Hola, ``name``!" ); // y es posible llamar a métodos String en la variable print ( " String.size es ``name.size``" ); } else if ( exists name ) { // "name" ahora tiene tipo Object en este bloque print ( "Hola, objeto ``name``!" ); } else { print ( "Hola, mundo!" ); } }hola ( nulo ); hola ( 1 ); hola ( "John Doe" );y que produce:
¡Hola, mundo! ¡Hola, objeto 1! ¡Hola, John Doe! String.size es 8Kotlin
Este ejemplo está en Kotlin :
fun hello ( obj : Any ) { // Una conversión de tipo falla si `obj` no es una cadena obj como cadena// Dado que la conversión de tipo no falló, `obj` debe ser una cadena . val l = obj.lengthprintln ( "' $ obj ' es una cadena de longitud $ l " ) } hello ( "Mooooo" )Beneficios
Esta técnica, junto con la inferencia de tipos, reduce la necesidad de escribir anotaciones de tipo para todas las variables o de realizar conversiones de tipo , como ocurre con los lenguajes dinámicos que utilizan tipado dinámico . Reduce la verbosidad y genera un código más conciso, más fácil de leer y modificar.
También puede ayudar a los implementadores de lenguajes a proporcionar implementaciones que ejecuten lenguajes dinámicos más rápidamente prediciendo el tipo de objetos de forma estática. [ 1 ]
Finalmente, aumenta la seguridad de tipos y puede prevenir problemas debidos a punteros nulos , etiquetados por CAR Hoare —el inventor de la referencia nula— como "el error de mil millones de dólares" [ 2 ].
Desde la perspectiva de los lenguajes de programación, es razonable afirmar que el tipado sensible al flujo es la característica que finalmente permitió construir lenguajes de programación utilizables y seguros en cuanto a tipos, con tipos de unión y sin comprobaciones dinámicas excesivas. Hasta este momento, los intentos de añadir esta característica a lenguajes como Scheme generalmente resultaban en representaciones de tipos excesivamente grandes. Un ejemplo de un sistema con soporte limitado para tipos de unión es Soft Scheme de Wright y Cartwright . [ 3 ]
Historia e implementaciones
La historia de la idea se remonta al menos a los "discriminadores de clase de registro" de Tony Hoare de mediados de la década de 1960. [ 4 ] Sin embargo, los sistemas de tipos prácticos que la incluyen como característica son mucho más recientes.
Typed Scheme, un sistema de tipos para Scheme desarrollado por Sam Tobin-Hochstadt y publicado por primera vez en 2008, fue el primer sistema de tipos en incluir la tipificación de ocurrencias. [ 5 ] Su sucesor, Typed Racket (un dialecto de Racket ), también se basa en la tipificación de ocurrencias. [ 6 ] Poco después de Typed Scheme, David J. Pearce reinventó de forma independiente la tipificación de flujo en Whiley . [ 7 ] [ 8 ]
Typed JavaScript observó que en los lenguajes de "scripting", el tipado de flujo depende de algo más que predicados condicionales; también depende del estado y del flujo de control. [ 9 ] Este estilo se ha adoptado desde entonces en lenguajes como Ceylon , [ 10 ] TypeScript [ 11 ] y Facebook Flow. [ 12 ]
También hay algunos lenguajes que no tienen tipos de unión pero sí tipos anulables , que tienen una forma limitada de esta característica que solo se aplica a los tipos anulables, como C# , [ 13 ] Kotlin , [ 14 ] [ 15 ] y Lobster. [ 16 ]
Alternativas
La coincidencia de patrones alcanza los mismos objetivos que la tipificación sensible al flujo: reducir la verbosidad y generar código más conciso, fácil de leer y modificar. Lo logra de una manera diferente: permite hacer coincidir el tipo de una estructura y extraer datos de ella simultáneamente mediante la declaración de una nueva variable. De esta forma, reduce la complejidad de la conversión de tipos y la extracción de valores. La coincidencia de patrones funciona mejor cuando se utiliza junto con tipos de datos algebraicos, ya que todos los casos pueden enumerarse y ser verificados estáticamente por el compilador.
Vea este ejemplo simulado para Java: [ 17 ]
int eval ( Nodo n ) {return switch ( n ) {// Intenta convertir el tipo "Node" a "IntNode" y crea la variable "i" de tipo "int".// Si eso funciona, entonces devuelve el valor de "i".caso IntNode ( int i ) -> i ;// intenta convertir el tipo "Node" a "NegNode" y crea la variable "n" de tipo "Node".// Si eso funciona, entonces devuelve la negación de evaluar el nodo "n".caso NegNode ( Nodo n ) -> - eval ( n );// Intenta convertir el tipo "Node" a "AddNode" y crea las variables "left" y "right" de tipo "Node".// Si eso funciona, entonces devuelve la suma de la evaluación de los nodos "izquierdo" y "derecho".caso AddNode ( Nodo izquierda , Nodo derecha ) -> eval ( izquierda ) + eval ( derecha );// Intenta convertir el tipo "Node" a "MulNode" y crea las variables "left" y "right" de tipo "Node".// Si funciona, entonces devuelve el producto de evaluar los nodos "izquierdo" y "derecho".caso MulNode ( Nodo izquierda , Nodo derecha ) -> eval ( izquierda ) * eval ( derecha );// No hay "predeterminado" porque el compilador sabe que se han enumerado todos los casos posibles.};}En un lenguaje de tipado estático, la ventaja de la coincidencia de patrones sobre el tipado sensible al flujo es que el tipo de una variable siempre permanece igual: no cambia según el flujo de control. Al escribir el patrón que se va a comparar, se declara una nueva variable que tendrá el nuevo tipo.
Referencias
- ↑ Lukas Eder (11 de diciembre de 2014). "La incómoda verdad sobre la tipificación dinámica frente a la estática" . blog.jooq.org . Consultado el 11 de marzo de 2016 .
- ↑ Tony Hoare (25/08/2009). "Referencias nulas: El error de mil millones de dólares" . InfoQ.com.
Lo llamo mi error de mil millones de dólares. Fue la invención de la referencia nula en 1965. En aquel entonces, estaba diseñando el primer sistema de tipos completo para referencias en un lenguaje orientado a objetos (
ALGOL W
). Mi objetivo era garantizar que todo uso de referencias fuera absolutamente seguro, con comprobaciones realizadas automáticamente por el compilador. Pero no pude resistir la tentación de incluir una referencia nula, simplemente porque era muy fácil de implementar. Esto ha dado lugar a innumerables errores, vulnerabilidades y fallos del sistema, que probablemente han causado mil millones de dólares en pérdidas y daños en los últimos cuarenta años.
- ↑ Wright, Andrew; Cartwright, Robert (1 de enero de 1997). "Un sistema práctico de tipos suaves para Scheme" . ACM Transactions on Programming Languages and Systems . 19 (1): 87–152 . doi : 10.1145/239912.239917 . Recuperado el 4 de mayo de 2024 .
- ↑ Hoare, CAR (12-16 de septiembre de 1966). Manejo de registros (PDF) . Escuela de Verano de la OTAN. Villard-de-Lans . Recuperado el 16 de agosto de 2025. pág. 18, sección 3.4:
El resultado de usar una construcción de esta forma es que cualquiera de las secciones alternativas del programa que se seleccione en tiempo de ejecución, el traductor sabe en tiempo de compilación qué subclase de registro es realmente referenciada por la variable e en el momento en que se inicia la ejecución de esa sección del programa. Por lo tanto, dentro de cada una de las secciones del programa, la variable e puede usarse de forma segura en los designadores de campo para campos privados de la subclase correspondiente, exactamente como si hubiera sido restringida por declaración para apuntar solo a registros de esa subclase.
- ↑ "Diseño e implementación de esquemas tipados: POPL" . dl.acm.org . 2008.
- ↑ "5 Tipado de ocurrencia" . docs.racket-lang.org .
- ↑ David J. Pearce (22 de septiembre de 2010). "Sobre los tipos sensibles al flujo en Whiley" . whiley.org. Archivado del original el 11 de marzo de 2016. Recuperado el 11 de marzo de 2016 .
- ↑ David J. Pearce (8 de abril de 2012). "Whiley - Flow Typing" . whiley.org. Archivado del original el 11 de marzo de 2016. Recuperado el 11 de marzo de 2016 .
- ↑ "Tipificación del control y el estado local mediante análisis de flujo" . Consultado el 14 de noviembre de 2016 .
- ↑ "Ceylon - Introducción rápida - Tipado seguro para valores nulos y sensible al flujo" . ceylon-lang.org . Consultado el 11 de marzo de 2016 .
- ↑ Ryan Cavanaugh (18 de noviembre de 2014). "Avance de TypeScript 1.4: tipos de unión, protecciones de tipo y más" . blogs.msdn.microsoft.com . Consultado el 11 de marzo de 2016 .
- ↑ Avik Chaudhuri; Basil Hosmer; Gabriel Levi (18 de noviembre de 2014). "Flow, un nuevo verificador de tipos estático para JavaScript" . code.facebook.com . Consultado el 11 de marzo de 2016 .
- ↑ "Diseño con tipos de referencia anulables " . docs.microsoft.com
- ↑ "Seguridad nula" . kotlinlang.org . Consultado el 11 de marzo de 2016 .
- ↑ "Type Checks and Casts" . kotlinlang.org . Consultado el 11 de marzo de 2016 .
- ↑ "El sistema de tipos de langosta" . aardappel.github.io .
- ↑ Gavin Bierman y Brian Goetz (19 de septiembre de 2023). "JEP 441: Pattern Matching for switch" . openjdk.org . Consultado el 14 de noviembre de 2023 .
- Sistemas de tipos
- Tipos de datos
- Análisis del programa
- teoría de tipos
- Comparación de lenguajes de programación