8           Sistemas de Tipos Polimorfismo de inclusão. Polimorfismo paramétrico. Sobrecarga. Conversões de tipos. No...
Polimorfismo de inclusão (1) Polimorfismo de inclusão é um sistema de tipo no qual um  tipo pode ter subtipos, que herdam...
Exemplo: tipos primitivos e subtipos em Ada   Declarações em Ada definindo subtipos de Integer     •   subtype Natural is...
Exemplo: tipos e subtipo de registros     discriminados em Ada (1) Declaração de registro discriminado em Ada   •   type ...
Exemplo: tipos e subtipo de registros     discriminados em Ada (2) Conjunto de valores dos subtipos        – Point = poin...
Polimorfismo de inclusão (2) Em linguagens de programação que suportam subtipos, não  é possível estabelecer univocamente...
Polimorfismo de inclusão (3)      Compatibilidade de tipos na presença de subtipos            • V := Etipo = T2          ...
Classes e subclasses Uma classe C é um conjunto de objetos equipados com  operações   • Cada objeto da classe C tem uma o...
Exemplo: subclasses de Java               como subtipos (1) Considere as classes   •   class Point {         protected do...
Exemplo: subclasses de Java           como subtipos (2) Conjuntos de valores das classes       – Point = Point(Double × D...
Exemplo: subclasses de Java            como subtipos (3) Java permite que objetos de qualquer subclasse sejam  tratados c...
Exemplo: extensibilidade em Java (1) Sejam as declarações considerando o exemplo anterior   •   class Line extends Point ...
Exemplo: extensibilidade em Java (2) • como também definem os tipos de classe estendidos abaixo     – Point$ = Point(Doubl...
Polimorfismo paramétrico (1) Polimorfismo paramétrico é um sistema de tipos que  permite que se escrevam procedimentos po...
Polimorfismo paramétrico (2) Procedimentos polimórficos   • Suportado naturalmente em Haskell       – Basta que se declar...
Exemplo: função polimórfica em Haskell (1)   Função monomórfica em Haskell     •   second (x: Int, y: Int) = y     • Tipo...
Exemplo: função polimórfica em Haskell (2)     • Agora, a chamada da função abaixo é legal         – second(13, true)    r...
Polimorfismo paramétrico (3) Um politipo deriva uma família de tipos e sempre possui  uma ou mais variáveis de tipo   ∀ σ...
Exemplo: função polimórfica em Haskell Considere a seguinte função monomórfica em Haskell   •   either (b: Bool) (x1: Cha...
Tipos parametrizados Um tipo parametrizado é um tipo que toma outros tipos  como parâmetros   • Pode-se imaginar o τ[] co...
Exemplo: tipo parametrizado em Haskell  Definição de um tipo de pares homogêneos    •   type Pair τ =(τ, τ)    •   τ   é ...
Exemplo: tipo parametrizado recursivo em                 Haskell  Definição de listas homogêneas                   List<τ...
Inferência de tipos A maioria das linguagens estaticamente tipadas requerem que  o programador declare explicitamente os ...
Exemplo: inferência de tipos monomórficos               em Haskell   Considere a seguinte definição de função     •   eve...
Exemplo: inferência de tipos polimórficos em                  Haskell    Considere a seguinte função      •   f . g =    ...
Sobrecarga (1) Um identificador está sobrecarregado se ele denota dois  ou mais procedimentos num mesmo escopo   • Aceitá...
Exemplo: funções sobrecarregadas em C O operador "–" em C denota simultaneamente quatro  funções pré-definidas   • Negaçã...
Exemplo: funções sobrecarregadas em Ada  O operador "/" denota duas funções pré-definidas em Ada    • Divisão de inteiros...
Sobrecarga (2) Suponha um identificador F denotando simultaneamente  duas funções f1: S1 → T1 e f2: S2 → T2   • Sobrecarg...
Sobrecarga (3) Advertência!   • Não confundir sobrecarga – também conhecido como     polimorfismo ad hoc – com polimorfis...
Conversão de tipos (1) Uma conversão de tipos é um mapeamento de valores de  um tipo para valores correspondentes de outr...
Conversão de tipos (2) Conversões de tipos podem ser mapeamentos totais   • Caracteres para códigos de caracteres, inteir...
Conversão de tipos (3) Conversões de tipos podem ser explícitas ou implícitas   • Um cast é uma conversão de tipos explíc...
Exemplo: coerções e Pascal e casts em Ada  Pascal    •   var n: Integer; x : Real;        ...        x := n;            -...
Notas de Implementação (1) Implementação do polimorfismo paramétrico   • Procedimento polimórficos operam uniformemente s...
Notas de Implementação (2) Considere as funções polimórficas   • Estas funções somente copia valores do tipo τ e isto é i...
Upcoming SlideShare
Loading in …5
×

08.type systems

234 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
234
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

08.type systems

  1. 1. 8 Sistemas de Tipos Polimorfismo de inclusão. Polimorfismo paramétrico. Sobrecarga. Conversões de tipos. Notas de implementação. 7-1
  2. 2. Polimorfismo de inclusão (1) Polimorfismo de inclusão é um sistema de tipo no qual um tipo pode ter subtipos, que herdam as operações daquele tipo • Conceito-chave das linguagem orientadas a objetos Tipos e subtipos • Um tipo T é um conjunto de valores equipado com operações • Um subtipo de T é um subconjunto dos valores de T equipado com as mesmas operações de T – Todo valor do subtipo também é valor do tipo T, podendo ser usado no contexto onde um valor do tipo T é esperado – O conceito de subtipos é uma idéia útil e comum em programação 7-2
  3. 3. Exemplo: tipos primitivos e subtipos em Ada  Declarações em Ada definindo subtipos de Integer • subtype Natural is Integer range 0 .. Integerlast; subtype Small is Integer range -3 .. +3; • Conjuntos de valores correspondentes – Natural = {0, 1, 2, 3, 4, 5, 6, ...} Small = {-3, -2, -1, 0, +1, +2, +3} • Variáveis i := n; Requer verificação em – i: Integer; i := s; tempo de execução n: Natural; s: Small; n := i; n := s; s := i; s := n: 7-3
  4. 4. Exemplo: tipos e subtipo de registros discriminados em Ada (1) Declaração de registro discriminado em Ada • type Form is (pointy, circular, rectangular); type Figure (f: Form := pointy) is record x, y: Float; case f is when pointy => null; when circular => r: Float; when rectangular => h, w: Float; end case; end record; subtype Point is Figure(pointy); subtype Circle is Figure(circular); subtype Rectangle is Figure(rectangular); • Conjuntos de valores – Form = {pointy, circular, rectangular} Figure = pointy(Float × Float) + circular(Float × Float × Float) + rectangular(Float × Float × Float × Float) 7-4
  5. 5. Exemplo: tipos e subtipo de registros discriminados em Ada (2) Conjunto de valores dos subtipos – Point = pointy(Float × Float) Circle = circular(Float × Float × Float) Rectangle = rectangular(Float × Float × Float × Float) Exemplos de variáveis • diagram: array (...) of Figure; frame: Rectangle; cursor: Point; 7-5
  6. 6. Polimorfismo de inclusão (2) Em linguagens de programação que suportam subtipos, não é possível estabelecer univocamente o subtipo de um valor Propriedades gerais dos subtipos • Condição necessária para S ser um subtipo de T: S ⊆ T • O tipo T é equipado com operações que são aplicáveis a todos os valores do tipo T • Todas essas operações também serão aplicáveis aos valores do subtipo S – S herda as perações de T 7-6
  7. 7. Polimorfismo de inclusão (3)  Compatibilidade de tipos na presença de subtipos • V := Etipo = T2 tipo = T1 • T1 é compatível com T2 se e somente se T1 e T2 têm valores em comum – Ou T1 é um subtipo de T2 ou T2 é um subtipo de T1 ou T1 e T2 são subtipos de outro tipo qualquer • Casos de interesse quando T1 e T2 são compatíveis – T1 é um subtipo de T2: T1 pode ser usado com segurança num contexto onde um valor de T2 é esperado – não precisa de verificação em tempo de execução – T1 não é um subtipo de T2: valores do tipo T1 somente podem ser usados após verificação em tempo de execução para determinar se ele também é um valor do tipo T2 7-7
  8. 8. Classes e subclasses Uma classe C é um conjunto de objetos equipados com operações • Cada objeto da classe C tem uma ou mais variáveis componentes e está equipada com métodos que acessam essas variáveis • Cada objeto de uma subclasse S de C herda todas as variáveis componentes dos objetos da classe C, podendo ter variáveis componentes adicionais • Cada objeto da classe S potencialmente herda todos os métodos dos objetos da classe C e pode ser equipada com métodos adicionais para acessar tais variáveis • Qualquer objeto de uma subclasse pode ser usado no contexto onde um objeto da classe C é esperado Subclasses não são o mesmo que subtipos em geral 7-8
  9. 9. Exemplo: subclasses de Java como subtipos (1) Considere as classes • class Point { protected double x, y; public void draw () {...} } class Circle extends Point { private double r; public void draw () {...} } class Rectangle extends Point { private double w, h; public void draw () {...} } 7-9
  10. 10. Exemplo: subclasses de Java como subtipos (2) Conjuntos de valores das classes – Point = Point(Double × Double) Circle = Circle(Double × Double × Double) Rectangle = Rectangle(Double × Double × Double × Double) • Nota-se que Circle e Rectangle não são subtipos de Point • Mas são subtipos do tipo Point$ abaixo – Point$ = Point(Double × Double) + Circle(Double × Double × Double) + Rectangle(Double × Double × Double × Double) Point$ 7-10
  11. 11. Exemplo: subclasses de Java como subtipos (3) Java permite que objetos de qualquer subclasse sejam tratados como objetos da superclasse • Point p; ... p= new Point(3.0, 4.0); ... p= new Circle(3.0, 4.0, 5.0); – Variáveis do classe Point podem referenciar quaisquer valores do tipo Point$ • Em geral, se C é uma classe, então C$ inclui não apenas os objetos da classe C, mas também os objetos de todas as subclasses de C – C$ é denominada de tipo de classe estendido (class-wide type) 7-11
  12. 12. Exemplo: extensibilidade em Java (1) Sejam as declarações considerando o exemplo anterior • class Line extends Point { private double length, orientation; public void draw () { ... } } class Textbox extends Rectangle { private String text; public void draw () { ... } } • As definições acima não apenas definem os conjuntos de objetos das novas classes – Line = Line(Double × Double × Double) Textbox = Textbox(Double × Double × String) 7-12
  13. 13. Exemplo: extensibilidade em Java (2) • como também definem os tipos de classe estendidos abaixo – Point$ = Point(Double × Double) + Circle(Double × Double × Double) + Rectangle(Double × Double × Double × Double) + Line(Double × Double × Double) + Textbox(Double × Double × String) – Rectangle$ = Rectangle(Double × Double × Double × Double) + Textbox(Double × Double × String) 7-13
  14. 14. Polimorfismo paramétrico (1) Polimorfismo paramétrico é um sistema de tipos que permite que se escrevam procedimentos polimórficos • Um procedimento polimórfico é aquele que pode operar uniformemente sobre argumentos de toda uma família de tipos • Um procedimento monomórfico é aquele que somente pode operar sobre argumentos de um tipo fixo – Uma função que computar o tamanho de uma lista de caracteres não pode ser aplicada a listas de inteiros, por exemplo 7-14
  15. 15. Polimorfismo paramétrico (2) Procedimentos polimórficos • Suportado naturalmente em Haskell – Basta que se declare o tipo da função utilizando variáveis de tipo, em vez de tipos específicos  Uma variável de tipo é um identificador que denota um tipo de uma família de tipos » Letras romanas minúsculas em Haskell » Letras gregas nos exemplos do livro-texto (α, β, γ, ...) 7-15
  16. 16. Exemplo: função polimórfica em Haskell (1)  Função monomórfica em Haskell • second (x: Int, y: Int) = y • Tipo da função – Integer × Integer → Integer – second(13, 21) resulta no valor 21, mas a chamada second(13, true) é ilegal, pois os tipos não casam  A versão polimórfica desta função resolve este problema • second (x: σ, y: τ) = y • Tipo da função – σ×τ→τ – σ e τ são variáveis de tipo, cada uma representação de um tipo arbitrário 7-16
  17. 17. Exemplo: função polimórfica em Haskell (2) • Agora, a chamada da função abaixo é legal – second(13, true) resulta no valor true – O tipo da função determinado pelo casamento de tipos é o seguinte  σ  Integer e τ  Boolean  Substituíndo ambos no tipo σ × τ → τ, tem-se o tipo resultante da aplicação da função  Integer × Boolean → Boolean  Esta função polimórfica aceitas argumentos de muitos tipos, mas não quaisquer argumentos • Apenas pares da forma σ × τ • As chamadas abaixo são ilegais – second(13) – second(1978, 5, 5) 7-17
  18. 18. Polimorfismo paramétrico (3) Um politipo deriva uma família de tipos e sempre possui uma ou mais variáveis de tipo ∀ σ × τ e σ × τ → τ são exemplos de politipos • Politipos derivam uma família de tipos pela substituição sistemática das variáveis de tipo por tipos – A família de tipos derivada por σ × τ → τ, incluem  Integer × Boolean → Boolean  String × String → String – mas não incluem  Integer × Boolean → Integer  Integer → Integer  String × String × String → String 7-18
  19. 19. Exemplo: função polimórfica em Haskell Considere a seguinte função monomórfica em Haskell • either (b: Bool) (x1: Char) (x2: Char) = if b then x1 else x2 • O tipo dessa função é Boolean → Character → Character → Character • translate (x: Char) = either (isspace x) x * • Nota-se que o tipo do primeiro argumento deve ser Boolean, mas os tipos do segundo e terceiro argumentos não precisão ser! • either (b: Bool) (x1: τ) (x2: τ) = if b then x1 else x2 • O tipo da função para Boolean → τ → τ → τ • translate (x: Char) = either (isspace x) x * • max (m: Int, n: Int) = Aplicação legal da função either (m > n) m n either 7-19
  20. 20. Tipos parametrizados Um tipo parametrizado é um tipo que toma outros tipos como parâmetros • Pode-se imaginar o τ[] como um tipo parametrizado de C, que pode ser especializado num tipo ordinário, como char[], ou float[], ou float[][], pela substituição da variável τ por um tipo real • Todas as linguagens de programação têm tipos parametrizados pré- definidos – C, C++ e Java têm τ[] – Ada tem array (σ) of e access τ • Poucas linguagens de programação, como ML e Haskell permitem que o programador defina seus próprios tipos parametrizados 7-20
  21. 21. Exemplo: tipo parametrizado em Haskell  Definição de um tipo de pares homogêneos • type Pair τ =(τ, τ) • τ é um parâmetro do tipo e denota um tipo arbitrário • Uma especialização deste tipo poderia ser – Pair Int – com o tipo resultante Integer × Integer, ou seja valores pares de números inteiros – ou – Pair Float – com o tipo resultante Real × Real, ou seja valores pares de números reais 7-21
  22. 22. Exemplo: tipo parametrizado recursivo em Haskell  Definição de listas homogêneas List<τ> = Unit + (τ × List<τ>) • data List τ = Nil | Cons (τ, List τ) • Agora, List Int é um tipo cujos valores são listas de inteiros • head (l: List τ)= head : List<τ> → τ case l of Nil -> ... -- error Cons(x,xs) -> x tail (l: List τ)= tail : List<τ> → List<τ> case l of Nil -> ... -- error Cons(x,xs) -> xs length (l: List τ)= length: List<τ> → Integer case l of Nil -> 0 Cons(x,xs) -> 1 + length(xs) 7-22
  23. 23. Inferência de tipos A maioria das linguagens estaticamente tipadas requerem que o programador declare explicitamente os tipos usados em toda entidade declarada num programa • I: constant T := E; I: T; function I (I: T) return T; Em Haskell, os tipos não precisam ser explicitados – são inferidos do contexto Inferência de tipos é um processo pelo qual o tipo de uma entidade declarada é inferido, quando não foi explicitamente estabelecido 7-23
  24. 24. Exemplo: inferência de tipos monomórficos em Haskell  Considere a seguinte definição de função • even n = (n mod 2 = 0) • Sabe-se que o operador mod tem tipo Integer × Integer → Integer • A partir da subexpressão n mod 2 pode-se inferir que o tipo de n é Integer • De forma similar, pode-se inferir que o corpo da função tem o tipo Boolean • Logo, a função even tem o monotipo Integer → Boolean • Nem sempre pode inferir um monotipo! 7-24
  25. 25. Exemplo: inferência de tipos polimórficos em Haskell  Considere a seguinte função • f . g = x -> f(g(x)) • Nota-se que f e g são funções, que o tipo resultante de g deve ser o mesmo tipo do parâmetro de f, mas nada se pode inferir que tipo é este, ele é um politipo β qualquer • Nada se pode afirmar dos tipos do parâmetro de g e do valor resultante de f, que podem ser diferentes, eles podem ter os politipos α e γ, respectivamente • Neste caso, o tipo de x obrigatoriamente é α • Logo, o tipo da função acima é – (β → γ) → (α → β) → (α → γ) 7-25
  26. 26. Sobrecarga (1) Um identificador está sobrecarregado se ele denota dois ou mais procedimentos num mesmo escopo • Aceitável apenas quando a chamada do procedimento pode ser feita sem ambigüidade Pode ser definida pelo programador em Java, C++, Ada, dentre outras linguagens 7-26
  27. 27. Exemplo: funções sobrecarregadas em C O operador "–" em C denota simultaneamente quatro funções pré-definidas • Negação de inteiros (função com tipo Integer → Integer) • Negação de ponto-flutuante (função com tipo Float → Float) • Subtração de inteiro (função com tipo Integer × Integer → Integer) • Subtração de ponto-flutuante (função com tipo Float × Float → Float) Nota-se que não há ambigüidade, pois o número e os tipos dos operandos determinam a função a chamar 7-27
  28. 28. Exemplo: funções sobrecarregadas em Ada  O operador "/" denota duas funções pré-definidas em Ada • Divisão de inteiros (função com tipo Integer × Integer → Integer) • Divisão de ponto-flutuante (função com tipo Float × Float → Float)  Pode-se declarar a função sobrecarregada abaixo • function "/" (m, n: Integer) return Float; -- Return the quotient of m and n as a real number. • cujo tipo é (função com tipo Integer × Integer → Float) • Neste caso, a função a chamar vai depender do contexto onde é aplicada e não apenas dos parâmetros reais • n: Integer; x: Float; ... • x := 7.0/2.0; - computes 7.0/2.0 = 3.5 x := 7/2; - computes 7/2 = 3.5 n := 7/2; - computes 7/2 = 3 n := (7/2)/(5/2); - computes (7/2)/(5/2) = 3/2 = 1 • Algumas chamadas são ambíguas e ilegais – x := (7/2)/(5/2); - computes (7/2)/(5/2) = 3/2 = 1.5 ou 7-28 (7/2)/(5/2) = 3.5/2.5 = 1.5
  29. 29. Sobrecarga (2) Suponha um identificador F denotando simultaneamente duas funções f1: S1 → T1 e f2: S2 → T2 • Sobrecarga independente de contexto: requer que S1 e S2 sejam não equivalentes – A função a ser chamada sempre pode ser univocamente identificada pelo tipo do parâmetro real • Sobrecarga dependente de contexto: requer apenas que S1 e S2 sejam não equivalentes ou que T1 e T2 sejam não equivalentes – Quando S1 e S2 são equivalentes, o contexto onde o F é utilizado deve ser levado em conta para identificar a função a ser chamada – Possibilita a formulação de expressões nas quais a função a ser chamada não pode ser determinada univocamente – tais construções ambíguas devem ser proibidas 7-29
  30. 30. Sobrecarga (3) Advertência! • Não confundir sobrecarga – também conhecido como polimorfismo ad hoc – com polimorfismo paramétrico – A sobrecarga não aumenta o poder de expressividade da linguagem de programação, visto que a quantidade de funções sobrecarregadas sempre é limitada – Por outro lado, o polimorfismo paramétrico permite que um procedimento opere sobre um número potencialmente ilimitado de variedade de tipos 7-30
  31. 31. Conversão de tipos (1) Uma conversão de tipos é um mapeamento de valores de um tipo para valores correspondentes de outro tipo • Exemplos: – Mapeamento natural de números inteiros para reais  {..., −2 → −2.0, −1 → −1.0, 0 → 0.0, +1 → +1.0, +2 → +2.0, ...} – Truncamento e o arredondamento são opções de conversão de tipos de real para inteiros – Mapeamento de caracteres para strings de tamanho 1 7-31
  32. 32. Conversão de tipos (2) Conversões de tipos podem ser mapeamentos totais • Caracteres para códigos de caracteres, inteiros para reais, ... Conversões de tipos podem ser mapeamentos parciais • Códigos de caracteres para caracteres, reais para inteiros, ... • Mapeamentos parciais podem provocar falhas em programas 7-32
  33. 33. Conversão de tipos (3) Conversões de tipos podem ser explícitas ou implícitas • Um cast é uma conversão de tipos explícita – Em C, C++ e Java, um cast tem a forma (T)E – Em Ada e em Object Pascal, um cast tem a forma T(E)  Caso a subexpressão E é do tipo S – não equivalente a T – e se a linguagem de programação define uma conversão de tipo de S para T, então o cast mapeia o valor de E para o valor correspondente de T • Uma coerção é uma conversão de tipos implícita, realizada automaticamente sempre que o contexto sintático exigir – Considere a expressão E no contexto do tipo T  Se o tipo de E é S – não equivalente a T – e se a linguagem permite a coerção de S para T nesse contexto, então a coerção mapeia o valor de E para o valor correspondente de T 7-33
  34. 34. Exemplo: coerções e Pascal e casts em Ada  Pascal • var n: Integer; x : Real; ... x := n; -- converte o valor de n para um número real n := x; -- ilegal! n := round(x) -- converte o valor de x para um inteiro por arredondamento  Ada • Ada não prover nenhum tipo de coerção • n: Integer; x : Float; ... x := n; -- ilegal! n := x; -- ilegal! x := Float(n) -- converte o valor de n para um número real n := Integer(x) -- converte o valor de x para um inteiro por arredondamento  Algumas linguagens de programação são muitos permissivas em relação às coerções, e. g. C, Java  A tendência é reduzir ou mesmo eliminar coerções nas linguagens de programação mais modernas 7-34
  35. 35. Notas de Implementação (1) Implementação do polimorfismo paramétrico • Procedimento polimórficos operam uniformemente sobre argumento de uma família de tipos • Opções de implementação – Gera uma instância separada de procedimento para cada tipo distinto de argumento  Inviável na prática! – Representar os valores de todos os tipos de tal modo que eles pareçam similar para o procedimento polimórfico  Representar cada valor através de um apontador para o objeto na memória heap 7-35
  36. 36. Notas de Implementação (2) Considere as funções polimórficas • Estas funções somente copia valores do tipo τ e isto é implementado copiando-se os ponteiros para estes valores – either: Boolean → τ → τ → τ – second: σ × τ → τ – head : List<τ> → τ • Esta implementação de funções paramétricas é simples e uniforme, porém de alto custo – Todos os valores – incluindo valores primitivos – devem ser armazenados na memória heap e acessados através de apontadores – Espaço na memória deve ser alocado quando eles são usados da primeira vez e desalocado pelo gerenciador de memória quando não são mais necessários 7-36

×