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 st 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 PD;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.