En ciencias de la computación, un sistema de tipos define cómo un lenguaje de programación clasifica los valores y las expresiones en tipos, cómo se pueden manipular estos tipos y cómo interactúan. Un tipo de dato indica un conjunto de valores que tienen el mismo significado genérico o propósito (aunque algunos tipos, como los tipos de datos abstractos y tipos de datos función tal vez no representen valores en el programa que se está ejecutando). Los sistemas de tipificación varían significativamente entre lenguajes, siendo quizás las más importantes variaciones las que estén en sus propias implementaciones de la sintáctica en tiempo de compilación y la operativa en tiempo de ejecución.
Un compilador puede usar el tipo estático de un valor para optimizar el almacenamiento que necesita y la elección de los algoritmos para las operaciones sobre ese valor. Por ejemplo, en muchos compiladores de C el tipo de dato "flotante" se representa en 32 bits, de acuerdo con la especificación IEEE para los números de coma flotante de simple precisión. Entonces, C usa operaciones específicas de coma flotante sobre estos valores (suma de coma flotante, multiplicación, etc.).
El rango del tipo de dato limita y la forma de su evaluación afecta en el "tipado" del lenguaje. Además, un lenguaje de programación puede asociar una operación concreta con diferentes algoritmos para cada tipo de dato en el caso del polimorfismo. En matemáticas y lógica, la teoría de tipos es el estudio de los sistemas de tipos, aunque los sistemas de tipos de datos concretos de los lenguajes de programación se originaron a partir de los problemas técnicos de las arquitecturas del ordenador, implementación del compilador y diseño del lenguaje.
Básicos
Asignar tipos de datos (tipificar) da significado a colecciones de bits. Los tipos de datos normalmente tienen asociaciones tanto con valores en la memoria o con objetos como con variables. Como cualquier valor simplemente consiste en un conjunto de bits de un ordenador, el hardware no hace distinción entre dirección de memoria, código de instrucción, caracteres, enteros y números en coma flotante. Los tipos de datos informan a los programas y programadores cómo deben ser tratados esos bits.
Las principales funciones que los sistemas de tipificación ofrecen son:
- Seguridad - El uso de tipos de datos puede permitir a un compilador detectar incoherencias en el significado o código probablemente inválido. Por ejemplo, podemos identificar una expresión
3 / "Hello, World"
como inválida porque no se puede dividir (de forma normal) un entero por una cadena de caracteres. Un sistema de tipado fuerte ofrece más seguridad, pero no garantiza, necesariamente una seguridad completa (ver seguridad de la tipificación para más información). - Optimización - Un sistema de tipado estático puede dar información muy útil al compilador. Por ejemplo, si un tipo de dato dice que un valor debe alinearse en múltiplos de 4, el compilador puede usar de forma más eficiente las instrucciones máquina.
- Documentación - En sistemas de tipificación más expresivos, los tipos de datos pueden servir como una forma de documentación, porque pueden ilustrar la intención del programador. Por ejemplo, los ítem de tiempos pueden ser un subtipo de un entero, pero si un programador declara una función como que devuelve ítems de tiempo en lugar de un simple entero, esto documenta parte del significado de la función.
- Abstracción (o modularidad) - Los tipos de datos permiten a los programadores pensar en los programas a un alto nivel, sin tener que preocuparse con el bajo nivel de la implementación. Por ejemplo, los programadores pueden pensar en una cadena de caracteres como un valor en lugar de un simple array de bytes. O los tipos de datos pueden permitir a los programadores expresar la Interfaz entre dos subsistemas. Esto localiza las definiciones requeridas para la interoperabilidad de los subsistemas y previene de inconsistencias cuando estos subsistemas se comuniquen.
Un programa normalmente asocia cada valor con un tipo de dato determinado (aunque un tipo de dato puede tener más de un subtipo). Otras entidades, como los objetos, bibliotecas, canales de comunicación, dependencias o, incluso, los propios tipos de datos, pueden ser asociados con un tipo de dato. Por ejemplo:
- Tipo de dato - un tipo de dato de un valor
- clase - un tipo de dato de un objeto
Un sistema de tipado, especificado en cada lenguaje de programación, estipula las formas en que los programas pueden ser escritos y hace ilegal cualquier comportamiento fuera de estas reglas.
Chequeo de tipificación
El proceso de verificar e imponer los límites impuestos por los tipos de datos –comprobación (chequeo) de tipificación– puede ocurrir tanto en la compilación (una comprobación estática) o en la ejecución (una comprobación dinámica). Si un lenguaje impone fuertemente las reglas de tipificación (es decir, generalmente permitiendo solo las conversiones de tipo de dato automáticas que no hagan perder información), uno se puede referir al proceso como fuertemente tipado; si no, débilmente tipado.
Tipado estático
Se dice de un lenguaje de programación que usa un tipado estático cuando la comprobación de tipificación se realiza durante la compilación, y no durante la ejecución. Ejemplos de lenguajes que usan tipado estático son C, C++, Java y Haskell. Comparado con el tipado dinámico, el estático permite que los errores de tipificación sean detectados antes, y que la ejecución del programa sea más eficiente y segura.
Tipado dinámico
Se dice de un lenguaje de programación que usa un tipado dinámico cuando la comprobación de tipificación se realiza durante su ejecución en vez de durante la compilación. Ejemplos de lenguajes que usan tipado dinámico son Perl, Python y Lisp. Comparado con el tipado estático, o sistema de enlazado temprano, el tipado dinámico es más flexible (debido a las limitaciones teóricas de la decidibilidad de ciertos problemas de análisis de programas estáticos, que impiden el mismo nivel de flexibilidad que se consigue con el tipado dinámico), a pesar de ejecutarse más lentamente y ser más propensos a contener errores de programación.
Tipado estático y dinámico combinados
Algunos lenguajes estáticamente tipados tienen una "puerta trasera" en el lenguaje que permite a los programadores escribir código que no es comprobado estáticamente. Por ejemplo, los lenguajes como Java y los parecidos al C tienen una "conversión de tipos de datos forzada (cast)"; estas operaciones pueden ser inseguras durante la ejecución, porque pueden causar comportamientos indeseados cuando el programa se ejecuta. Otros lenguajes de programación como C# utilizan declaraciones de tipos de datos dinámicos en un ambiente estático, permitiendo la flexibilidad casi completa de un lenguaje dinámico en un lenguaje estático.
La presencia de un tipado estático en un lenguaje de programación no implica necesariamente la ausencia de mecanismos de tipado dinámico. Por ejemplo, Java usa tipado estático, pero ciertas operaciones requieren el soporte de test de tipos de datos durante la ejecución, que es una forma de tipado dinámico. Ver lenguaje de programación para una discusión más amplia de la interacción entre tipado estático y dinámico.
Chequeo de tipificación estático y dinámico en la práctica
La elección entre sistemas de tipificación dinámico y estático requiere algunas contraprestaciones.
El tipado estático busca errores en los tipos de datos durante la compilación. Esto debería incrementar la fiabilidad de los programas procesados. Sin embargo, los programadores, normalmente, están en desacuerdo en cómo los errores de tipos de datos más comunes ocurren, y en qué proporción de estos errores que se han escrito podrían haberse cazado con un tipado estático. El tipado estático aboga por la creencia de que los programas son más fiables cuando son chequeados sus tipos de datos, mientras que el tipado dinámico apunta al código distribuido que se ha probado que es fiable y un conjunto pequeño de errores. El valor del tipado estático, entonces, se incrementa a la par que se endurece el sistema de tipificación. Los defensores de los lenguajes fuertemente tipados como ML y Haskell han sugerido que casi todos los errores pueden ser considerados errores de los tipos de datos, si los tipos de datos usados en un programa están suficientemente bien declarados por el programador o inferidos por el compilador.[1]
El tipado estático resulta, normalmente, en un código compilado que se ejecuta más rápidamente. Cuando el compilador conoce los tipos de datos exactos que están en uso, puede producir código máquina optimizado. Además, los compiladores en los lenguajes de tipado estático pueden encontrar atajos más fácilmente. Algunos lenguajes de tipificación dinámica como el lisp permiten declaraciones de tipos de datos opcionales para la optimización por esta misma razón. El tipado estático generaliza este uso. Ver optimización de software
En contraste, el tipado dinámico permite a los compiladores e intérpretes ejecutarse más rápidamente, debido a que los cambios en el código fuente en los lenguajes dinámicamente tipados puede resultar en menores comprobaciones y menos código que revisar. Esto también reduce el ciclo editar-compilar-comprobar-depurar.
Lenguajes estáticamente tipados que no dispongan de inferencia (como Java), requieren que el programador declare los tipos de datos que un método o función puede procesar. Esto puede hacer la veces de documentación adicional del programa, que el compilador no permitirá que el programador ignore o drift out of synchronization. Sin embargo, un lenguaje puede ser de tipificación estática sin requerir la declaración del tipo de dato (ejemplos incluyen Scala y C#3.0), así que esto no es una consecuencia de tipificación estática.
El tipado dinámico permite construcciones que algunos sistemas de tipado estático rechazarían al considerarlas ilegales. Por ejemplo, la función eval de Python, la cual ejecuta datos arbitrarios como si fueran código. Además, el tipado dinámico es más adecuado para código de transición o para el prototipado. Desarrollos recientes en lenguajes como Haskell (por ejemplo, los tipos algebraicos generalizados), permiten a lenguajes de tipado estático, ejecutar código a partir de estructuras de datos de una forma segura.
El tipado dinámico típicamente hace que la metaprogramación sea menos verbosa. Por ejemplo, los genéricos de C++ requiere una escritura más descriptiva que su equivalente en Ruby o Python, precisamente porque es necesario especificar los tipos implicados.[cita requerida]
Controversia
Hay frecuentemente conflictos entre aquellos que prefieren la tipificación fuerte y/o estática y aquellos que se inclinan por la tipificación dinámica, libre o débil. El primer grupo aboga por la detección temprana de errores durante la compilación y el aumento de rendimiento en tiempo de ejecución, mientras que el segundo grupo aboga por los prototipos rápidos que son posibles con un sistema de tipificación dinámico.[2]