Oh hombre, estoy emocionado de tratar de responder esta pregunta lo mejor que puedo. Espero poder ordenar mis pensamientos correctamente.
Como @Doval mencionó y el interrogador señaló (aunque groseramente), realmente no tiene un sistema de tipos. Tiene un sistema de comprobaciones dinámicas que utiliza etiquetas, que en general es mucho más débil y también mucho menos interesante.
La cuestión de "qué es un sistema de tipos" puede ser bastante filosófica, y podríamos llenar un libro con diferentes puntos de vista sobre el tema. Sin embargo, como este es un sitio para programadores, trataré de mantener mi respuesta lo más práctica posible (y realmente, los tipos son extremadamente prácticos en la programación, a pesar de lo que algunos puedan pensar).
Visión de conjunto
Comencemos con una comprensión del asiento de los pantalones de para qué sirve un sistema de tipos, antes de sumergirse en las bases más formales. Un sistema de tipos impone estructura a nuestros programas . Nos dicen cómo podemos conectar varias funciones y expresiones juntas. Sin estructura, los programas son insostenibles y extremadamente complejos, listos para causar daño ante el más mínimo error del programador.
Escribir programas con un sistema tipo es como conducir un cuidado en perfecto estado: los frenos funcionan, las puertas se cierran de manera segura, el motor está engrasado, etc. Escribir programas sin un sistema tipo es como conducir una motocicleta sin casco y con ruedas hechas fuera de espagueti. No tienes absolutamente ningún control sobre tu.
Para fundamentar la discusión, digamos que tenemos un lenguaje con expresión literal num[n]
y str[s]
que representa el número n y la cadena s, respectivamente, y funciones primitivas plus
y concat
, con el significado deseado. Claramente, no quieres poder escribir algo como plus "hello" "world"
o concat 2 4
. Pero, ¿cómo podemos evitar esto? A priori , no existe un método para distinguir el número 2 de la cadena literal "mundo". Lo que nos gustaría decir es que estas expresiones deben usarse en diferentes contextos; Tienen diferentes tipos.
Idiomas y tipos
Retrocedamos un poco: ¿qué es un lenguaje de programación? En general, podemos dividir un lenguaje de programación en dos capas: la sintaxis y la semántica. También se denominan estática y dinámica , respectivamente. Resulta que el sistema de tipos es necesario para mediar la interacción entre estas dos partes.
Sintaxis
Un programa es un árbol. No se deje engañar por las líneas de texto que escribe en una computadora; estas son solo las representaciones legibles por humanos de un programa. El programa en sí es un árbol de sintaxis abstracta . Por ejemplo, en C podríamos escribir:
int square(int x) {
return x * x;
}
Esa es la sintaxis concreta para el programa (fragmento). La representación del árbol es:
function square
/ | \
int int x return
|
times
/ \
x x
Un lenguaje de programación proporciona una gramática que define los árboles válidos de ese lenguaje (se puede usar sintaxis concreta o abstracta). Esto generalmente se hace usando algo como la notación BNF. Supongo que has hecho esto para el idioma que has creado.
Semántica
Bien, sabemos lo que es un programa, pero es solo una estructura de árbol estática. Presumiblemente, queremos que nuestro programa realmente calcule algo. Necesitamos semántica.
La semántica de los lenguajes de programación es un rico campo de estudio. En términos generales, hay dos enfoques: semántica denotacional y semántica operativa . La semántica de denominación describe un programa al mapearlo en alguna estructura matemática subyacente (por ejemplo, los números naturales, funciones continuas, etc.). eso le da sentido a nuestro programa. La semántica operacional, por el contrario, define un programa al detallar cómo se ejecuta. En mi opinión, la semántica operativa es más intuitiva para los programadores (incluyéndome a mí), así que sigamos con eso.
No explicaré cómo definir una semántica operativa formal (los detalles son un poco complicados), pero básicamente queremos reglas como las siguientes:
num[n]
es un valor
str[s]
es un valor
- Si
num[n1]
y num[n2]
evalúa los enteros n_1$ and $n_2$, then
más (num [n1], num [n2]) `evalúa el entero $ n_1 + n_2 $.
- Si
str[s1]
y se str[s2]
evalúa en las cadenas s1 y s2, se concat(str[s1], str[s2])
evalúa en la cadena s1s2.
Etc. Las reglas son en la práctica mucho más formales, pero entiendes la esencia. Sin embargo, pronto nos encontramos con un problema. Qué sucede cuando escribimos lo siguiente:
concat(num[5], str[hello])
Hm. Esto es todo un enigma. No hemos definido una regla en ninguna parte sobre cómo concatenar un número con una cadena. Podríamos intentar crear dicha regla, pero intuitivamente sabemos que esta operación no tiene sentido. No queremos que este programa sea válido. Y así nos llevan inexorablemente a los tipos.
Tipos
Un programa es un árbol definido por la gramática de un lenguaje. Los programas reciben significado por las reglas de ejecución. Pero algunos programas no pueden ejecutarse; es decir, algunos programas no tienen sentido . Estos programas están mal escritos. Por lo tanto, la escritura caracteriza programas significativos en un idioma. Si un programa está bien escrito, podemos ejecutarlo.
Demos algunos ejemplos. Nuevamente, al igual que con las reglas de evaluación, presentaré las reglas de mecanografía de manera informal, pero pueden hacerse rigurosas. Aquí hay algunas reglas:
- Una ficha del formulario
num[n]
tiene tipo nat
.
- Una ficha del formulario
str[s]
tiene tipo str
.
- Si expresión
e1
tiene tipo nat
y expresión e2
tiene tipo nat
, entonces la expresión plus(e1, e2)
tiene tipo nat
.
- Si la expresión
e1
tiene tipo str
y la expresión e2
tiene tipo str
, entonces la expresión concat(e1, e2)
tiene tipo str
.
Por lo tanto, de acuerdo con estas reglas, hay un plus(num[5], num[2])
tipo has nat
, pero no podemos asignarle un tipo plus(num[5], str["hello"])
. Decimos que un programa (o expresión) está bien escrito si podemos asignarle cualquier tipo, y de lo contrario está mal escrito. Un sistema de tipos es válido si se pueden ejecutar todos los programas bien escritos. Haskell es sano; C no lo es.
Conclusión
Hay otras opiniones sobre tipos. Los tipos en cierto sentido corresponden a la lógica intuicionista, y también pueden verse como objetos en la teoría de categorías. Comprender estas conexiones es fascinante, pero no es esencial si uno simplemente quiere escribir o incluso diseñar un lenguaje de programación. Sin embargo, comprender los tipos como una herramienta para controlar las formaciones de programas es esencial para el diseño y desarrollo del lenguaje de programación. Solo he arañado la superficie de qué tipos pueden expresar. Espero que pienses que vale la pena incorporarlo a tu idioma.