Sistema de tipos
Transcript of Sistema de tipos
Sistema de tipos
Fundamentos de Computación II
SISTEMA DE TIPOS
Un tipo describe un conjunto de valores. Ej: Números: 1, 2, PI, etc. Operaciones: +,-,*. etc.
Un sistema de tipos define cómo un lenguaje de programación clasifica los valores y las
expresiones en tipos que tienen sentido para ser utilizados en una operación dada.
Al asignarle un tipo a una expresión se está delimitando cuál es el conjunto de valores
que podría tener esa expresión. Las operaciones computacionales en general no pueden
ser aplicadas sobre cualquier valor.
Ejemplo:
• x e y : valores numéricos
• x e y :valores booleanos La expresión x + y ¿tiene sentido?
La idea de tipo permite relacionar:
✓un conjunto de valores que tienen ese tipo o son de ese tipo,
✓con las operaciones que pueden ser realizadas sobre esos valores.
La expresión x + y ¿tiene sentido?
¿Y EN OBJETOS?
En objetos se trabaja objetos y mensajes.
Los son los objetos y las los mensajes.
Conceptos
Tipado: Es la definición del tipo que tendrá una variable dada. Ejemplo si en una variable
queremos guardar numeros enteros, lo mejor es tener una variable de tipo int.
Lenguaje tipado: es el que no permite violación de los datos.
Lenguaje no tipado: Al definir una variable no es necesario definir el tipo.
Puede cambiar en cualquier momento. Ejemplo: pseudocódigo , PHP. En éste lenguaje no
hay ningún problema en que una variable sea una Array y luego se convierta en un entero
o un String. Lisp es un lenguaje sin tipificación.
para (i <- 1; i<=100; i++)
{ establecer numero a verdadero;
si i es divisible por 3 escribir “HOLA";
establecer numero a falso;
si i es divisible por 5 escribir “hola";
establecer numero a falso;
si numero, escribir i; escribir una nueva línea; }
Hay una variable declarada i asociada a un contador pero no se sabe de que tipo es la variable. Si revisamos el código nos damos cuenta que i al ser inicializada recibe el numero 1, pero no nos preocupamos por que tipo de valor recibe la variable, solo sabemos que proceso se quiere hacer, pero si deseamos pasar el pseudocódigo a programación real, en el caso de C que es fuertemente tipado, debemos decirle que la variable es un entero.
Tipado débil
No se indica el tipo de variable al declararla. Se puede asignar, por ejemplo, un valor entero a
una variable que anteriormente tenía una cadena. También se puede operar aritméticamente
con variables de distintos tipos: ej: sumar “x” + 5. Lenguajes que lo usan: PHP, java script
Ventajas Desventajas
Nos olvidamos de declarar el tipo Al hacer operaciones, a veces éstas salen mal. Se pueden cometer muchos errores.
Podemos cambiar el tipo de la variable sobre la marcha. Por ejemplo, asignarle un string a un int
“20” > “100” dará como resultado true, ya que son comparados como cadena, no como números.
Escribimos menos código Hay que castear
Es más rápido de desarollar Al declarar los argumentos de una función no sabemos si ésta espera un flotante, un entero, un string, etc. Tenemos que ir a la función, ver lo que hace e inferir el tipo de variable que espera.
Quien infiere los tipos es el propio lenguaje
Ejemplo en JavaScript
Una diferencia con el tipado fuerte es que el tipado débil se puede asignar, por
ejemplo, un valor entero a una variable que anteriormente tenía una cadena.
Tipado fuerte
Se le indica el tipo de dato al declarar la variable. Dicho tipo no puede ser cambiado
nunca. Y no se puede operar entre distintos tipos. Lenguajes: Ruby, Python,…
Ventajas Desventajas
Código expresivo: por ejemplo en una función se sabe de qué tipo espera un argumento una función
Escribir más código: hay que declarar el tipo de variable.
Menos errores: Nos olvidamos de ver el tipo de variable antes de hacer operaciones con ésta
Ejemplo: si realizamos la operación de “x” + 5 en Python:
Los de tipado fuerte son mucho más seguros, porque no permiten hacer operaciones con
variable de distintos tipos y eso permite que no se comentan tantos errores, lo que puede
ser conveniente para proyectos de gran envergadura.
Los de tipado débil se puede trabajar mucho más rápido, lo que permite obtener resultados
antes, lo que es bueno cuando se está en fase de ideación o de proyectos más pequeños.
Según de qué proyecto se trate se usa un tipado u otro.
¿Cuál es mejor?
Tipado Dinámico: el tipo de dato se comprueba en tiempo de ejecución. No es
necesario definir los tipos al declarar una variable. Ejemplo: PHP, JavaScript,
Grooby, Python, Perl.
Tipado Estático: el tipo de dato lo define el programador, se comprueba una vez
que se compila el programa. El tipado estático nos obliga a definir desde el
principio el tipo de una variable, Ejemplo: C, C++, Pascal, Java.
✓Ayudar a detectar errores al programar.
✓Guiar al programador sobre las operaciones válidas en un determinado contexto, tanto
en cuanto a documentación como en cuanto a ayudas automáticas que puede proveer
por ejemplo un IDE.
✓En algunos casos el comportamiento de una operación puede variar en función del
tipo de los elementos involucrados en la misma. De esto vamos a diferenciar varios
sabores: polimorfismo, sobrecarga, multimétodos, etc.
Algunas características de un sistema de tipos:
✓Proveen información al programador, de forma más precisa que un comentario.
✓Un sistema de tipos debería permitir hacer validaciones utilizando la información de
tipos, algorítmicamente.
✓Las validaciones basadas en un sistema de tipos, son más fácilmente automatizables
que otros tipos de especificaciones formales.
¿Para qué sirve el sistema de tipos?
Conversiones
Conversión implícita: cuando el valor que se va a almacenar puede ajustarse a la variable sin
necesidad de truncamiento o redondeo.
Ejemplo: una variable de tipo long entero de 8 bytes puede almacenar cualquier valor que
pueda almacenar a su vez un elemento int : 4 bytes en un equipo de 32 bits.
int num = 21474833647;
long Num = num;
El compilador convierte implícitamente el valor de la derecha en un tipo long antes de asignarlo a Num.
Conversión explicita o conversión de tipo:
Una conversión de tipo es una manera de informar al compilador de forma explicita de que
pretende realizar la conversión y que esta al tanto de que puede producirse una perdida de
datos. Para realizar una conversión de tipo, se debe especificar entre paréntesis el tipo al
que se va a aplicar dicha conversión delante del valor o la variable que se va a convertir.
int main(){
double x;int a=3;// Cast int a double.x = a/2 ;cout << x;
}
La salida es: 1
int main(){
double x;int a=3;// Cast int a double.x = (double) a/2 ;cout << x;
}
La salida es: 1,5
Razones para verificar tipos estática (en tiempo de compilación)
1. Eficiencia de ejecución: la información de tipos estáticos permite a los compiladores
asignar memoria con eficiencia y generara código de máquina que manipula los datos
eficientemente.
2.- Eficiencia de traducción: un compilador puede utilizar los tipos estáticos a fin de
reducir la cantidad de código que necesita compilar.
3.- Capacidad de escritura: muchos errores estándar de programación son detectados
rápidamente.
4.- Mejora la seguridad, la confiabilidad y la legibilidad.
5.- Mejora el desarrollo de los programas grandes al verificar la consistencia y corrección
de la interfaz.
Se pueden producir dos tipos de errores durante la ejecución de un programa:
✓ Trapped (atrapados)
Son los errores que se detectan inmediatamente, por ejemplo una división por cero.
✓Untrapped (no atrapados)
Son errores que pueden no ser detectados. El programa podría continuar ejecutándose
por un tiempo antes de detectar el problema.
Por ejemplo si se accede a posiciones de un array más allá de su longitud o se salta a una
posición inválida de memoria.
Todos los errores de tipo se detectan en tiempo de compilación.
Detección de errores
Momento del checkeo
Se refiere a la capacidad del lenguaje de verificar que una operación es válida para un
objeto dado.
1. Chequeo estático
Se realiza antes de la ejecución (por ejemplo, los realiza el compilador).
Ejemplos "puros": Haskell y Scala hacen todos sus chequeos en forma estática.
Ejemplos "impuros": Java y C hacen chequeos en forma estática, pero esto no es
todo lo que pasa.
2. Chequeo dinámico
Se realiza durante la ejecución.
Al encontrarse un error de tipos durante la ejecución / evaluación, el lenguaje lo
detecta y modela.
Un ejemplo que conocemos todos es Smalltalk. El modelo de error de tipo es el
DoesNotUnderstand. Otros ejemplos: Python, Ruby.
3. No chequea en ningún momento
Al encontrarse un error de tipos durante la ejecución / evaluación, el lenguaje no
lo detecta, y las consecuencias son impredecibles.
Tipos nominales y estructurales
Tipado nominal
En un sistema de tipos nominal, dos tipos son compatibles si tienen el mismo
nombre, independientemente de que tengan los mismos elementos o no.
En un sistema de tipos nominal, se puede tener dos tipos con «idénticos» valores
posibles, pero que no son compatibles entre sí.
class Person {
public int Id { get; set; }
public string Name { get; set; }
}
class Product {
public int Id { get; set; }
public string Name { get; set; }
}
Person paco = new Person { Id = 2, Name = "Paco" };
Product morcilla = paco; // error!
Person y Product son estructuralmente idénticos y pueden tomar
exactamente los mismos valores, concretamente, todos los posibles
pares de int y string. Sin embargo, para el sistema de tipos de C#, los
elementos de un conjunto no son compatibles con los del otro
1234
typedef char CHARACTER;
char c1 = 'a';CHARACTER c2 = c2; // OK, es sólo un alias, no un nuevo tipo
Hay lenguajes que permiten definir alias sobre tipos, de forma que podamos referirnos a
un tipo con varios nombres, pero sin considerarlos tipos diferentes.
Por ejemplo, en C podemos usar typedef.
Tipado estructural:
Se basa en la estructura de los tipos.
En un sistema de tipos estructural dos tipos son compatibles si la estructura de sus
elementos es compatible, es decir que el tipo desde el que estamos asignando contiene, al
menos, la misma información que el tipo al que estamos asignando.
Smalltalk o Ruby, el tipado es naturalmente estructural. Por ejemplo:
#Titiretero
darFuncion: titeretitere decir: 'Hola!'titere levantarBrazotitere decir: 'Adios!'
No especifica de qué tipo es "titere", ya que no se declaran los tipos de las variables
Dentro del cuerpo del método le envía dos mensajes: decir y levantarBrazo. Estas son las características que se espera
del títere en este método.Implícitamente se está definiendo que el objeto títere tiene que cumplir con el tipo que define estos dos mensajes. Este tipo no tiene nombre, la especificación del tipo requerido
no es nominal.A eso se le llama DuckTyping!
TABLAS COMPARATIVAS
Momento del chequeo Mayormente estático, pero en presencia de chequeos se hace dinámico. Modelo de error de tipos en chequeo dinámico: ClassCastException.
Información de tipos que hay que escribir Mucha, está muy del lado explícito.En la "cultura Java" se considera esto una ventaja, se dice que facilita la comprensión de las interfaces y de los programas, para las personas que los tienen que usar y mantener.
Identificación de tipos Nominal.
Lenguajes similares C# (hasta donde sabemos)
JAVA
Momento del chequeo Dinámico, modelo de error de tipos: DoesNotUnderstand.
Información de tipos que hay que escribir
La mínima posible, está muy del lado implícito.La idea es minimizar la cantidad de "burocracia" en el código. El nombre del lenguaje es muy claro al respecto.
Identificación de tipos Naturalmente estructural : no se le pone nombre a los tipos.
Lenguajes similares Ruby (hasta donde sabemos)
SMALLTALK
Momento del chequeo Estático. Muy preocupado desde la concepción por evitar errores de tipos. Da herramientas para permitir polimorfismo.
Información de tipos que hay que escribir
Muy poca, está muy del lado implícito. Entre sus objetivos está aprovechar la inferencia de tipos todo lo que se pueda. En algunos (pocos) casos a la inferencia no le da la nafta, y el programador debe ayudarla poniendo algo de información de tipos.
Identificación de tipos En general nominal, hay mecanismos de tipado estructural provistos por el lenguaje (polimorfismo paramétrico).
Lenguajes similares no conocemos
HASKELL
Se plantearon tres clasificaciones:
✓En cuanto a la forma de chequeo: puede ser estático/compile time, dinámico/runtime o
nada.
✓En cuanto a la forma de especificar el tipo de algo : puede ser explícito o implícito /
✓En cuanto a la forma de constituir un tipo: puede ser nominal o estructural.
Resumen
¿Como seria un lenguaje de programación ideal?
✓Que tenga chequeo estático, para no tener que preocuparse por los errores de tipos,
que los verifique el lenguaje.
✓Que el tipado sea implícito, para escribir menos código.
✓Que la identificación de tipos sea estructural, para que permita aprovechar más el
polimorfismo, lo que implica que tengo que preocuparme menos por cuándo voy a
poder usar un objeto en un determinado contexto: si puedo el lenguaje me va a dejar
sin que tenga que hacer nada, si no puedo me va a dar error de tipos en forma estática
porque ya pedio chequeo estático.
Buscando el lenguaje perfecto
Un programador con una visión más "organizativa” podría preferir tipado explícito, e
incluso identificación nominal.
La razón es que aunque el programa queda más largo, y tal vez más difícil de armar, al
poner mucha información de tipos y ponerle un nombre a cada tipo que se está
manejando, hay mucha documentación que queda dentro mismo del programa.
Esta visión tiene adeptos particularmente en proyectos grandes, por los que pasan muchos
programadores.
A un programador con mucha autoconfianza podría preocuparle poco que el chequeo sea
estático, si igual sabe bien lo que está haciendo.
¿Y las conclusiones? Las decide cada uno.
EJERCICIO
1. Investigue como seria, en los lenguajes BASIC, COBOL, PASCAL, UNIX Y DBASE:
▪La declaración de las variables A,B y C de tipo entero.
2. Si A=2, B=3 y C=5
Investigar, en los mismos lenguajes, las operaciones:
▪A+B,
▪C-B,
▪B/A,
▪A*C y
▪A**B.