classe no idioma e tipo de POO

9

Na teoria da linguagem de programação, um tipo é um conjunto de valores. Por exemplo, o tipo "int" é o conjunto de todos os valores inteiros.

Nas linguagens OOP, uma classe é um tipo, é?

Quando uma classe é definida com mais de um membro, por exemplo,

class myclass{
    int a; 
    double b;
}

Quando falamos de uma aula, queremos dizer

  • " (a,b)onde aé um int e bé um duplo", ou
  • "{ (x,y)| xé int, yexiste um duplo}"?

O que significa uma instância de myclass?

  • " (a,b)onde aé um int e bé um duplo", ou
  • um objeto que ocupa um espaço de memória e que pode (não necessariamente, ou seja, pode estar vazio) armazenar (x,y), onde xexiste int e qual yé o dobro?
Tim
fonte
2
Uma classe é um tipo. "{(x, y) | x é qualquer int, y é um duplo}" " estaria quase correto, exceto por duas coisas: 1) você usou uma tupla enquanto uma classe é conceitualmente um registro - você se refere a campos por nome, não posição e 2) Nem todos os registros com campos ae bsão membros desse tipo, como menciona Killian Forth.Myclass é isomórfico para registros com campos ae bdo tipo inte double- você pode pegar um registro como esse e transformá-lo em uma instância de myclass.
Doval 21/01
11
Em idiomas fortemente tipados, uma classe é um tipo. Em idiomas de tipo fraco, pode ou não ser um tipo.
precisa saber é o seguinte
11
Na teoria da linguagem de programação, um tipo é um conjunto de valores? Eu acho que você precisa comprar outro livro ou outro professor ou ambos. Uma 'variável' ou 'constante' possui um 'tipo' e geralmente possui um 'valor'. Existem zero tipos de valor, escalares e tipos de valor de composição em que o valor da variável ou constante contém sub-variáveis ​​/ sub-constantes.
user1703394
11
@ user1703394 Um tipo é um conjunto de valores. Um tipo inteiro de 32 bits é um conjunto de 2 ^ 32 valores distintos. Se uma expressão for avaliada para um valor desse tipo, você saberá que esse valor está nesse conjunto. Os operadores matemáticos são apenas funções sobre os valores desse conjunto.
Doval
11
Eu também seria cauteloso ao considerar tipos como conjuntos de valores. Os conjuntos têm relações que não são estritamente aplicáveis ​​aos tipos. Para conceituar tipos, é um bom modelo, mas se decompõe quando você começa a analisar as coisas mais de perto - e mais ainda quando introduz subtipos.
Telastyn

Respostas:

30

Nem.

Suponho que você esteja se perguntando se ter o mesmo conjunto de tipos de campo é suficiente para classificar como sendo da mesma classe ou se eles precisam ser nomeados de forma idêntica. A resposta é: "Nem mesmo os mesmos tipos e nomes são suficientes!" Classes estruturalmente equivalentes não são necessariamente compatíveis com o tipo.

Por exemplo, se você tem um CartesianCoordinatese uma PolarCordinatesclasse, eles podem tanto ter dois números como os seus campos, e eles podem até ter o mesmo Numbertipo e os mesmos nomes, mas eles ainda não seria compatível, e uma instância PolarCoordinatesnão seria um instância de CartesianCoordinates. A capacidade de separar tipos pelo objetivo pretendido e não pela implementação atual é uma parte muito útil para escrever código mais seguro e mais sustentável.

Kilian Foth
fonte
9
Deve-se notar que, em algumas línguas, ser estruturalmente equivalente é suficiente para transformar um tipo em um subtipo do outro (e geralmente vice-versa). Embora isso seja decididamente incomum / impopular.
Telastyn
7
@ Tim Um typedef não cria um tipo, aliás, o nome usado para se referir a um tipo existente.
Doval
11
@ DevSolar Ele mencionou explicitamente C e, além de C ++, não conheço nenhuma outra linguagem que use essa palavra-chave.
Doval
3
@Telastyn - esses idiomas devem ser mortos com fogo.
Jon História
4
A subtipagem estrutural do @JonStory é útil no nível do módulo; a falta disso é o que obriga a transformar tudo interfaceem Java e C #, se você quiser fazer testes de unidade. Você acaba escrevendo uma tonelada de clichê para poder alterar a classe específica que seu programa usará, mesmo que você não tenha nenhuma intenção de alterá-lo em tempo de execução.
Doval
6

Tipos não são conjuntos.

Veja bem, a teoria dos conjuntos tem vários recursos que simplesmente não se aplicam aos tipos e vice-versa . Por exemplo, um objeto tem um único tipo canônico. Pode ser uma instância de vários tipos diferentes, mas apenas um desses tipos foi usado para instanciar. A teoria dos conjuntos não tem noção de conjuntos "canônicos".

A teoria dos conjuntos permite criar subconjuntos em tempo real , se você tiver uma regra que descreva o que pertence ao subconjunto. A teoria dos tipos geralmente não permite isso. Embora a maioria dos idiomas tenha um Numbertipo ou algo semelhante, eles não têm um EvenNumbertipo, nem seria fácil criar um. Quero dizer, é fácil definir o tipo em si, mas quaisquer Numbers existentes que sejam pares não serão magicamente transformados em EvenNumbers.

Na verdade, dizer que você pode "criar" subconjuntos é um tanto falso, porque conjuntos são um tipo diferente de animal. Na teoria dos conjuntos, esses subconjuntos já existem , de todas as maneiras infinitas que você pode defini-los. Na teoria dos tipos, geralmente esperamos lidar com um número finito (se grande) de tipos a qualquer momento. Os únicos tipos que dizem existir são aqueles que realmente definimos, e não todos os tipos que poderíamos definir.

Os conjuntos não têm permissão para se conter direta ou indiretamente . Algumas linguagens, como Python, fornecem tipos com estruturas menos regulares (no Python, typeo tipo canônico é typee objecté considerado uma instância de object). Por outro lado, a maioria dos idiomas não permite que tipos definidos pelo usuário se envolvam nesse tipo de truque.

Geralmente, os conjuntos podem se sobrepor sem estar contidos um no outro. Isso é incomum na teoria dos tipos, embora algumas linguagens a suportem na forma de herança múltipla. Outras linguagens, como Java, apenas permitem uma forma restrita disso ou o proíbem totalmente.

O tipo vazio existe (é chamado de tipo inferior ), mas a maioria dos idiomas não o suporta ou não o considera como um tipo de primeira classe. O "tipo que contém todos os outros tipos" também existe (é chamado de tipo superior ) e é amplamente suportado, diferentemente da teoria dos conjuntos.

NB : Como alguns comentadores apontaram anteriormente (antes da discussão do tópico), é possível modelar tipos com teoria dos conjuntos e outras construções matemáticas padrão. Por exemplo, você pode modelar a associação de tipo como uma relação, em vez de modelar tipos como conjuntos. Mas, na prática, isso é muito mais simples se você usar a teoria das categorias em vez da teoria dos conjuntos. É assim que Haskell modela sua teoria de tipos, por exemplo.


A noção de "subtipagem" é realmente bem diferente da noção de "subconjunto". Se Xfor um subtipo de Y, significa que podemos substituir instâncias de Ypor instâncias de Xe o programa ainda "funcionará" em algum sentido. Isso é comportamental e não estrutural, embora algumas linguagens (por exemplo, Go, Rust, sem dúvida C) tenham escolhido a última por razões de conveniência, seja para o programador ou para a implementação da linguagem.

Kevin
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
World Engineer
4

Os tipos de dados algébricos são a maneira de discutir isso.

Existem três maneiras fundamentais de combinar tipos:

  • Produtos. É basicamente o que você está pensando:

    struct IntXDouble{
      int a; 
      double b;
    }
    

    é um tipo de produto; seus valores são todas as combinações possíveis (ou seja, tuplas) de um inte um double. Se você considerar os tipos de número como conjuntos, a cardinalidade do tipo de produto é de fato o produto das cardinalidades dos campos.

  • Soma. Nas linguagens procedurais, é um pouco estranho expressar diretamente (classicamente, isso é feito com uniões marcadas ); portanto, para melhor entendimento, aqui está um tipo de soma no Haskell:

    data IntOrDouble = AnInt Int
                     | ADouble Double
    

    os valores desse tipo têm o formato AnInt 345ou ADouble 4.23, mas sempre há apenas um número envolvido (diferente do tipo de produto, em que cada valor tem dois números). Portanto, a cardinalidade: você primeiro enumera todos os Intvalores, cada um deve ser combinado com o AnIntconstrutor. Além disso , todos os Doublevalores, cada um combinado com ADouble. Daí o tipo de soma .

  • Exponenciação 1 . Não discutirei isso em detalhes aqui, porque não há nenhuma correspondência clara de OO.

E as aulas? Eu usei deliberadamente a palavra-chave structe não classpara IntXDouble. O fato é que uma classe como um tipo não é realmente caracterizada por seus campos, esses são apenas detalhes de implementação. O fator crucial é, antes, que valores distinguíveis a classe pode ter.

O que é relevante, porém, é que o valor de uma classe pode ser o valor de qualquer uma de suas subclasses ! Portanto, uma classe é na verdade um tipo de soma e não um tipo de produto: se Ae Bseriam derivados de myClass, o myClassseria essencialmente a soma de Ae B. Independentemente da implementação real.


1 São funções (no sentido matemático! ); um tipo de função Int -> Doubleé representado pelo exponencial DoubleInt. Muito ruim se o seu idioma não tem funções adequadas ...

leftaroundabout
fonte
2
Desculpe, mas acho que essa é uma resposta muito ruim. Funções fazer ter um análogo OO clara, nomeadamente métodos (e-tipos de interface método único). A definição básica de um objeto é que ele possui tanto estado (campos / membros de dados) quanto comportamento (métodos / funções de membro); sua resposta ignora o último.
Ruakh
@ruakh: não. É claro que você pode implementar funções no OO, mas, em geral, os métodos não são funções ( porque eles modificam o estado etc.). Tampouco são "funções" em linguagens procedurais, nesse caso. De fato, as interfaces de método estático único se aproximam mais dos tipos de função / exponencial, mas eu esperava evitar a discussão disso, porque não tem relevância para essa questão.
usar o seguinte comando
... o mais importante, a minha anwer faz considerar comportamento. De fato, o comportamento geralmente é a razão pela qual você usa herança, e a unificação de diferentes comportamentos possíveis captura com precisão o aspecto do tipo soma das classes OO.
usar o seguinte comando
@ruakh Um método não pode sem seu objeto. O analógico mais próximo são os staticmétodos, exceto que ainda não são valores de primeira classe. O que acontece com a maioria das linguagens OO é que elas aceitam objetos como o menor bloco de construção; portanto, se você deseja algo menor, precisa falsificá-lo com objetos e ainda acaba arrastando várias funções semânticas. Por exemplo, não faz sentido comparar funções para igualdade, mas você ainda pode comparar dois objetos de função falsa.
Doval
@Doval 1) você pode passar métodos pelo AFAIK, para que sejam valores de primeira classe; 2) faz sentido comparar funções para igualdade, as pessoas JS fazem isso o tempo todo.
Den
2

Desculpe, mas eu não sei sobre a teoria "bruta". Só posso fornecer uma abordagem prática. Espero que isso seja aceitável em programmers.SE; Não estou familiarizado com a etiqueta aqui.


Um tema central da OOP é a ocultação de informações . O que os membros de dados de uma classe são exatamente não devem interessar a seus clientes. Um cliente envia mensagens para (chama métodos / funções-membro de) uma instância, que pode ou não modificar o estado interno. A idéia é que os internos de uma classe possam mudar, sem que o cliente seja afetado por ela.

Um corrolário disso é que a classe é responsável por garantir que sua representação interna permaneça "válida". Vamos assumir uma classe que armazena um número de telefone (simplificado) em dois números inteiros:

    int areacode;
    int number;

Estes são os membros de dados da classe. No entanto, a classe provavelmente será muito mais do que apenas seus membros de dados e certamente não é definível como "conjunto de todos os valores possíveis de int x int". Você não deve ter acesso direto aos membros dos dados.

A construção de uma instância pode negar qualquer número negativo. Talvez a construção também normalize o código de área de alguma forma ou até verifique o número inteiro. Você teria, assim, acabar muito mais perto do seu "(a,b) where a is an int and b is a double", porque certamente não é qualquer dois int armazenado nessa classe.

Mas isso realmente não importa no que diz respeito à classe. Não é o tipo dos membros dos dados, nem o intervalo de seus possíveis valores que define a classe; são os métodos que são definidos para ela.

Enquanto esses métodos permanecerem os mesmos, o implementador poderá alterar os tipos de dados para ponto flutuante, BIGNUMs, seqüências de caracteres, o que for e para todos os fins práticos, ainda seria a mesma classe .


Existem padrões de design para garantir que tais alterações na representação interna possam ser feitas sem que o cliente esteja ciente disso (por exemplo, o idioma pimpl em C ++, que oculta qualquer membro de dados atrás de um ponteiro opaco ).

DevSolar
fonte
11
It is neither the type of the data members, nor the range of their possible values that defines the class, it's the methods that are defined for it.Os membros dos dados não definem uma classe apenas quando você os oculta. Esse pode ser o caso mais comum, mas certamente não se pode dizer que é verdade para todas as classes. Se um único campo é público, é tão importante quanto seus métodos.
Doval
11
A menos que você esteja codificando em Java, onde você não tem escolha e até mesmo seus registros falsos e sem comportamento, devem ser classes. (Marcá-los finalajuda a entender o ponto, mas ainda assim). Você ainda tem um problema com os protectedmembros, que podem ser herdados e, portanto, fazem parte de uma segunda API para implementadores de subclasses.
Doval
11
@Doval: Entendi que essa é uma questão "teórica", e é por isso que fiquei o mais claro possível dos problemas de linguagem reais. (Assim como eu ficar tão clara de Java e protectedquanto possível na prática ;-).)
DevSolar
3
O problema é que a classé uma construção dependente da linguagem. Até onde eu sei, não existe classteoria teórica.
Doval
11
@Doval: isso não significa que a teoria dos tipos em si não se aplica às classes, pois elas são uma construção fora do escopo dessa teoria?
DevSolar
2
  • Um tipo é uma descrição de uma categoria / faixa de valores, estruturas compostas ou o que você possui. OOPwise, é semelhante a uma "interface". (No sentido agnóstico da linguagem. O sentido específico da linguagem, nem tanto. Em Java, por exemplo, inté um tipo , mas não tem relação com uma interface. As especificações de campo público / protegido também não fazem parte de um interface, mas fazem parte de uma "interface" ou tipo .)

    O ponto principal é que é muito mais uma definição semântica do que concreta. Estruture apenas fatores na medida em que os campos / comportamentos expostos e seus propósitos definidos estejam alinhados. Se você não possui os dois, não possui compatibilidade de tipo.

  • Uma classe é a realização de um tipo. É um modelo que realmente define a estrutura interna, o comportamento anexado etc.

cHao
fonte