8 Sistemas de Tipos Polimorfismo de inclusão.

Slides:



Advertisements
Apresentações semelhantes
Estruturação de Linguagens (Pascal e C++)
Advertisements

Programação em Java Prof. Maurício Braga
Programação em Java Prof. Maurício Braga
Motivação para listas duplamente encadeadas e circulares
Paulo Marques Hernâni Pedroso
Métodos, Parâmetros, Argumentos e Contratos
14/10/09 Uma animação possui: Início; Passo; Fim; 1.
Interação entre objetos
Orientação a Objetos: Encapsulamento e Classificação
Java: Pacotes e Modificadores de Visibilidade
Relações Adriano Joaquim de O Cruz ©2002 NCE/UFRJ
Programação Básica em Java
1 Tipos definidos O programador pode definir seus próprios tipos de dados tipos complexos usados da mesma forma que os simples declaram-se variáveis utilizando-se.
1 Complexidade de Algoritmos Complexidade de pior caso Complexidade de melhor caso de uso bem menos freqüente em algumas situações específicas Complexidade.
Árvores.
Coleta de resíduos. Sumário Resíduos Coleta de resíduos Contador de referências Marcação e varredura Parada e cópia Marcação e compactação Gerenciamento.
Estruturas de Dados e Algoritmos
LINGUAGEM C.
Rafael Pinto Frederico Corrêa
Curso Sistemas de Informação Disciplina: Arquitetura de Software
Linguagem de Programação II
Estruturas de Dados II Prof.: Sergio Pacheco Prof.: Sergio Pacheco 1 1.
1 Professor Rogério B. de Andrade - Aula 001- Dez/2008 Técnicas e Linguagem de Programação.
9 Controle de fluxo Seqüenciadores. Jumps. Escapes. Exceções.
7 Abstração Genérica Unidades genéricas e instanciação.
00(c) 2007 Gustavo Motta1 Introdução ao -calculus Prof. Gustavo Motta Departamento de Informática/UFPB.
Paradigmas da Programação
Aula 4 Nomes, Vinculações, Tipos e Escopos
Aula 8 Orientação à Objetos
Aula 6 Subprogramas Universidade do Vale do Rio dos Sinos
Robson Godoi / Sandra Siebra
Programação Baseada em Objectos Desenho de TAD
Listas Encadeadas.
Revisão da Linguagem C.
Classes e objetos Arrays e Sobrecarga
Herança e Polimorfismo
Classes e objetos P. O. O. Prof. Grace.
Introdução a Programação JAVA
Análise Semântica e Representação Intermédia
PHP Tipos de dados Constantes.
Conceitos de Linguagem de Programação
UNIVERSIDADE FEDERAL DE UBERLÂNDIA ENGENHARIA CIVIL INTRODUÇÃO À ALGORITMOS Professora: Fabíola Gonçalves.
Programação Orientada à Objetos
Programação Funcional
Entendendo as definições de classe
Organização do Ambiente de Execução (Runtime System)
Tipos de Dados Paradigmas de Programação
Verificação de Tipos e Escopos
Estudo dos Conceitos e Paradigmas de Programação
Tipos Abstratos de Dados
Nomes, Variáveis e Vinculações
Expressões e Instrução de Atribuição
Linguagem Funcional 2 Linguagem Funcional 2 - LF2 Estende LF1 com funções de alta ordem Uma função passa a ser um valor O contexto inclui um único componente:
Organização do Ambiente de Execução (Runtime system) Representação de dados Avaliação de expressões Alocação de memória Rotinas Objetos e métodos.
Compilação de Linguagens OO Marco Simões (macs3) André Santos (alms)
1 2 Observa ilustração. Cria um texto. Observa ilustração.
Capítulo 5 Garbage Collector.
Ambiente de Execução - Rotinas
INTRODUÇÃO À ORIENTAÇÃO A OBJETOS EM JAVA
Wagner Santos C. de Jesus
UTFPR – Campus Curitiba - DAELN Cursos de Eng. Eletrônica/Eng
Campus de Caraguatatuba Aula 9: Noções Básicas sobre Erros (3)
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.
Certificação Marco Antonio. Introdução A compreensão desse capítulo é muito importante pois trata de um assunto essencial em qualquer linguagem de programação,
Programação II Prof. Mateus Raeder Universidade do Vale do Rio dos Sinos - São Leopoldo -
CES-10 INTRODUÇÃO À COMPUTAÇÃO
Orientação a Objetos e Java Graduação em Ciência da Computação  Centro de Informática, UFPE Alexandre Mota
VARIÁVEIS Desenvolvido pelo Mestrando Rodrigo Moreira Barreto – VHLab (Virtual Humans Simulation Laboratory – PUCRS) 1 Desenvolvido por: Rodrigo Moreira.
MATA56 Tipos, Verificação de Tipos Agenda Objetivo desta aula: Introduzir os principais aspectos semânticos de variáveis Palavras-chave – nomes.
Java Como Programar, 8/E Deitel/Deitel, 8e. Java – Como programar Copyright © 2010 Pearson Education Slide 1.
Transcrição da apresentação:

8 Sistemas de Tipos Polimorfismo de inclusão. Polimorfismo paramétrico. Sobrecarga. Conversões de tipos. Notas de implementação.

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

Exemplo: tipos primitivos e subtipos em Ada Declarações em Ada definindo subtipos de Integer subtype Natural is Integer range 0 .. Integer'last; 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: Integer; n: Natural; s: Small; i := n; i := s; Requer verificação em tempo de execução n := i; n := s; s := i; s := n:

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)

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;

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

Polimorfismo de inclusão (3) Compatibilidade de tipos na presença de subtipos V := E 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 tipo = T1 tipo = T2

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

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 () {...} }

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$

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)

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)

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)

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

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 (, , , ...)

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       e  são variáveis de tipo, cada uma representação de um tipo arbitrário

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)

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

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       max (m: Int, n: Int) = either (m > n) m n' Aplicação legal da função either

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

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

Exemplo: tipo parametrizado recursivo em Haskell Definição de listas homogêneas data List  = Nil | Cons (, List ) Agora, List Int é um tipo cujos valores são listas de inteiros head (l: List )= case l of Nil -> ... -- error Cons(x,xs) -> x tail (l: List )= case l of Nil -> ... -- error Cons(x,xs) -> xs length (l: List )= case l of Nil -> 0 Cons(x,xs) -> 1 + length(xs) List<> = Unit + (  List<>) head : List<>   tail : List<>  List<> length: List<>  Integer

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

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!

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 é (  )  (  )  (  )

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

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

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/2)/(5/2) = 3.5/2.5 = 1.5

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

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

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

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

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

Exemplo: coerções e Pascal e casts em Ada 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

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

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