El análisis de tipos tiene por objetivo asegurar que el uso de los objetos definidos es correcto: esto es, que su uso se atiene a la semántica de su definición; por ejemplo, que un array de enteros no es llamado como función o que no se intenta incrementar una función o que el valor retornado por una función es de la naturaleza descrita en su definición.
Si bien el sistema de tipos es una propiedad del lenguaje, no es raro que los compiladores introduzcan modificaciones en el sistema de tipos del lenguaje. Por ejemplo en Pascal el tipo de un array incluye los índices del array13.1). Esto y las reglas de equivalencia de tipos de Pascal limitan gravemente la genericidad de las funciones en Pascal. Por eso algunos compiladores Pascal permiten en una llamada a función la compatibilidad de tipos entre arrays de diferente tamaño y diferentes conjuntos de índices. Desgraciadamente la forma en la que lo hacen puede diferir de compilador a compilador.
Un lenguaje de programación tiene tipado dinámico si el lenguaje realiza comprobaciones de tipo en tiempo de ejecución. En un sistema de tipos dinámico los tipos suelen estár asociados con los valores no con las variables.
El lenguaje en el que se escribe el metaprograma se denomina metalenguaje. El lenguaje al que se traduce el metaprograma se denomina lenguaje objeto. La capacidad de un lenguaje de programación para ser su propio metalenguaje se denomina reflexividad. Para que haya reflexión es conveniente que el código sea un tipo de estructura de datos soportado por el lenguaje al mismo nivel que otros tipos básicos y que sea posible traducir dinámicamente texto a código.
Un símbolo se dice sobrecargado si su significado varía dependiendo del contexto. En la mayoría de los lenguajes Los operadores aritméticos suelen estar sobrecargados, dado que se sustancian en diferentes algoritmos según sus operandos sean enteros, flotantes, etc.
En algunos lenguajes se permite la sobrecarga de funciones.
así es posible tener dos funciones llamadas min
:
int min(int a, int b) { if (a < b) return a; return b; } |
int min(string a, string b) { if (strcmp(a, b) < 0) return a; return b; } |
A la hora de evaluar el tipo de las expresiones es el contexto de la llamada el que determina el tipo de la expresión:
float x,y; int a,b; string c,d; u = min(x,y); /* Puede que correcto: x e y seran truncados a enteros. Tipo entero */ v = min(a,b); /* Correcto: Tipo devuelto es entero */ w = min(c,d); /* Correcto: Tipo devuelto es string */ t = min(x,c); /* Error */
pl@nereida:~/src/perl/attributegrammar/Language-AttributeGrammar-0.08/examples$ ocaml Objective Caml version 3.09.2 # let minimo = fun i j -> if i<j then i else j;; val minimo : 'a -> 'a -> 'a = <fun> # minimo 2 3;; - : int = 2 # minimo 4.9 5.3;; - : float = 4.9 # minimo "hola" "mundo";; - : string = "hola"El compilador
OCaml
infiere el tipo de las expresiones.
Así el tipo asociado con la función minimo
es
'a -> 'a -> 'a
que es una expresión de tipo que contiene
variables de tipo. El operador ->
es asociativo a derechas
y asi la expresión debe ser leída como 'a -> ('a -> 'a)
.
Básicamente dice: es una función que toma un argumento de tipo 'a
(donde 'a
es una variable tipo que será instanciada en el momento del uso
de la función)
y devuelve una función que toma elementos de tipo 'a
y retorna elementos de
tipo 'a
.
minimo
fuera 'a x 'a -> 'a
, lo cierto es que en algunos lenguajes funcionales
es usual que todas las funciones sean consideradas como funciones
de una sóla variable. La función de dos variables 'a x 'a -> 'a
puede verse como una función 'a -> ('a -> 'a)
. En efecto la función minimo
cuando recibe un argumento retorna una función:
# let min_mundo = minimo "mundo";; val min_mundo : string -> string = <fun> # min_mundo "pedro";; - : string = "mundo" # min_mundo "antonio";; - : string = "antonio" # min_mundo 4;; This expression has type int but is here used with type string # min_mundo(string_of_int(4));; - : string = "4"Esta estrategia de reducir funciones de varias variables a funciones de una variable que retornan funciones de una variable se conoce con el nombre de currying o aplicación parcial.
Se conoce como función polimorfa a una función que puede ser aplicada o evaluada sobre diferentes tipos de datos.
Un tipo de datos se dice polimorfo si es un tipo de datos generalizado o no completamente especificado. Por ejemplo, una lista cuyos elementos son de cualquier tipo.
En el siguiente ejemplo en OCaml
construimos una función similar al map
de Perl.
La función mymap
ilustra el polimorfismo paramétrico: la función puede ser usada
con un número arbitrario de tipos, no hemos tenido que hacer ningún tipo de declaración explícita y sin embargo
el uso incorrecto de los tipos es señalado como un error:
# let rec mymap f list = match list with [] -> [] | hd :: tail -> f hd :: mymap f tail;; val mymap : ('a -> 'b) -> 'a list -> 'b list = <fun> # mymap (function n -> n*2) [1;3;5];; - : int list = [2; 6; 10] # mymap (function n -> n.[0]) ["hola"; "mundo"];; - : char list = ['h'; 'm'] # mymap (function n -> n*2) ["hola"; "mundo"];; This expression has type string but is here used with type int
La introducción de nombres para las expresiones de tipo introduce una ambiguedad en la interpretación de la equivalencia de tipos. Por ejemplo, dado el código:
typedef int v10[10]; v10 a; int b[10];¿Se considera que
a
y b
tienen tipos compatibles?
Si utilizamos la opción de sustituir los nombres por sus definiciones y permitimos en la definición de tipo el uso de nombres de tipo no declarados se pueden producir ciclos en el grafo de tipos.
El lenguaje C impide la presencia de ciclos en el grafo de tipos usando dos reglas:
struct
para las cuales se usa equivalencia nominal
Por ejemplo, el siguiente programa:
nereida:~/src/perl/testing> cat -n typeequiv.c 1 #include <stdio.h> 2 3 typedef struct { 4 int x, y; 5 struct record *next; 6 } record; 7 8 record z,w; 9 10 struct recordcopy { 11 int x, y; 12 struct recordcopy *next; 13 } r,k; 14 15 16 main() { 17 k = r; /* no produce error */ 18 z = w; /* no produce error */ 19 r = z; 20 }Produce el siguiente mensaje de error:
nereida:~/src/perl/testing> gcc -fsyntax-only typeequiv.c typeequiv.c: En la función 'main': typeequiv.c:19: error: tipos incompatibles en la asignación
En lenguajes dinámicos una forma habitual de equivalencia de tipos es el tipado pato:
El término hace referencia al llamado test del pato: If it waddles like a duck, and quacks like a duck, it's a duck!.