Conversão de Tipos
• Exemplo: x + i, onde x é do tipo real e i é
do tipo integer.
• A especificação da linguagem deve indicar
se a linguagem suporta este tipo de
expressão, e qual a conversão necessária.
• Conversões neste caso podem ser inseridas
pelo typechecker: (+.real) x (inttoreal (i))
Coerções
• Conversões de tipo implícitas são chamadas
de coerções.
• Coerções normalmente são limitadas a
conversões em que não há perda de
informação: inteiro para real, mas não o
contrário.
• Conversões explícitas são idênticas às
aplicações de funções normais.
Coerções (cont.)
• Exemplos de tratamento de conversões em
diferentes linguagens:
– Ada: sempre explícitas
– Pascal e Haskell não misturam caracteres e
inteiros, como ocorre em C.
– x é um array de float, há alguma diferença em
inicializá-lo nas formas abaixo?
for (i=0;i<n;i++) {x[i] = 1;}
for (i=0;i<n;i++) {x[i] = 1.0;}
Overloading de funções e
operadores
• Um símbolo é overloaded se o seu
significado depende do contexto. Exemplos:
– operador ‘+’ em floats, inteiros, números
complexos etc.
– ‘(‘ e ‘)’ em Ada: índice de arrays, chamada de
funções, conversão de tipos (cast).
• overloading é resolvido quando um valor
único para o símbolo é determinado.
Descobrindo os tipos de
operadores overloaded
• Ao invés de trabalharmos com o tipo de
uma expressão, podemos trabalhar com o
conjunto de tipos possíveis de uma
expressão.
• Na maioria das linguagens uma expressão
tem que ter (ao final da verificação) um
único tipo!
Descobrindo os tipos de
operadores overloaded (cont.)
E  E1
E  id
E  E1 (E2)
{ E.types = E1 .types }
{ E.types = lookup(id.entry) }
{ E.types =
{ t | s  E2 .types
 st  E1.types}}
Funções e operadores
polimórficos
•
•
procedimento ou função que pode ser
executado com argumentos de tipos
diferentes.
Exemplos built-in nas linguagens: índices
de arrays, aplicação de funções, operações
sobre ponteiros.
Por que funções polimórficas?
•
•
Facilitam a implementação de algorítmos
que manipulam estruturas de dados,
independentemente do tipo da estrutura.
Exemplo: Pascal (ou C) vs. Haskell
Exemplo em Pascal
•
type link = ^cell;
cell = record info: integer;
next: link;
end;
function length (lptr : link) : integer;
var len : integer;
begin len = 0;
while (lptr <> nil) do begin
len := len + 1;
lptr := lptr^.next;
end;
length := len;
end;
Exemplo em Haskell
•
length :: [t] -> Int;
length [] = 0
length (a:as) = 1 + length as
ou
• length :: [t] -> Int;
length lptr = if null lptr
then 0
else 1 + length (tail lptr)
Variáveis de tipos
•
•
servem para identificar um tipo que não é
conhecido em uma determinada parte do
programa.
Inferência de tipos permite determinar o
tipo a partir de seu uso, e.g. descobrir o tipo
de uma função a partir de sua definição.
Exemplo: map em pascal (ou C)
•
type link = ^cell;
function map (lptr : link; procedure p);
begin
while (lptr <> nil) do begin
p(lptr);
lptr := lptr^.next;
end;
end;
Exemplo: deref em pascal (ou C)
•
function deref (p);
begin
return p^;
end;
Estendendo a linguagem para
suportar polimorfismo
PD;E
D  D ; D | id : Q
Q   type_variable . Q | T
T  T ‘->’ T | T  T
| unary_constructor ( T )
| basic_type
| type_variable | ( T )
Regras para inferência de tipos
•
•
•
Ocorrências distintas de uma função
polimórfica em uma expressão podem ter
tipos diferentes.
Como variáveis podem aparecer em
expressões de tipos, temos que rever a noção
de equivalência de tipos. Usamos unificação
ao invés de equivalência.
Usar um mecanismo para guardar os
resultados da unificação dos tipos.
Substituições, instâncias e
unificação
•
•
•
substituições: mapeamento de variáveis de
tipos em expressões de tipos.
instâncias: tipos mais específicos são
instâncias de tipos mais genéricos.
expressões de tipo t1 e t2 unificam se existe
uma substituição S tal que S(t1) = S(t2).
Queremos o unificador mais geral, i.e. que
gere o mínimo de restrições nas variáveis nas
expressões.
Download

coerções