clase en lenguaje OOP y tipo

9

En la teoría del lenguaje de programación, un tipo es un conjunto de valores. Por ejemplo, el tipo "int" es el conjunto de todos los valores enteros.

En los lenguajes OOP, una clase es un tipo, ¿verdad?

Cuando una clase se define con más de un miembro, p. Ej.

class myclass{
    int a; 
    double b;
}

Cuando hablamos de una clase, ¿queremos decir

  • " (a,b)donde aes un int y bes un doble", o
  • "{ (x,y)| xes cualquier int, yes cualquier doble}"?

¿Qué significa una instancia de myclass?

  • " (a,b)donde aes un int y bes un doble", o
  • un objeto que ocupa un espacio de memoria y que puede (no necesariamente, es decir, puede estar vacío) almacenar (x,y), ¿dónde xhay algún int y ycualquier doble?
Tim
fuente
2
Una clase es un tipo. "{(x, y) | x es cualquier int, y es cualquier doble}" " sería casi correcto, excepto por dos cosas: 1) ha usado una tupla mientras que una clase es conceptualmente un registro - usted hace referencia a su campos por nombre, no por posición; y 2) No todos los registros con campos ay bson miembros de ese tipo, como menciona Killian Forth. Myclass es isomorfo a registros con campos ay bde tipo inty double- podría tomar un registro como ese y convertirlo en una instancia de myclass.
Doval
1
En idiomas fuertemente tipados, una clase es un tipo. En idiomas de tipo débil, puede o no ser un tipo.
shawnhcorey
1
En la teoría del lenguaje de programación, ¿un tipo es un conjunto de valores? Creo que necesitas conseguir otro libro u otro maestro o ambos. Una 'variable' o una 'constante' tiene un 'tipo' y a menudo tiene un 'valor'. Hay tipos de valores cero, escalares y tipos de valores compuestos donde el valor de la variable o constante contiene subvariables / subconstantes.
user1703394
1
@ user1703394 Un tipo es un conjunto de valores. Un tipo entero de 32 bits es un conjunto de 2 ^ 32 valores distintos. Si una expresión se evalúa como un valor de ese tipo, sabrá que ese valor está en ese conjunto. Los operadores matemáticos son solo funciones sobre los valores de ese conjunto.
Doval
1
También sería cauteloso considerando los tipos como conjuntos de valores. Los conjuntos tienen relaciones que no son estrictamente aplicables a los tipos. Para conceptualizar tipos es un buen modelo, pero se descompone una vez que comienzas a mirar las cosas más de cerca, e incluso más cuando introduces el subtipo.
Telastyn

Respuestas:

30

Ninguno.

Supongo que se pregunta si tener el mismo conjunto de tipos de campo es suficiente para clasificar como la misma clase, o si también deben nombrarse de manera idéntica. La respuesta es: "¡Ni siquiera tener los mismos tipos y los mismos nombres es suficiente!" Las clases estructuralmente equivalentes no son necesariamente compatibles con el tipo.

Por ejemplo, si tiene una CartesianCoordinatesy una PolarCordinatesclase, ambos podrían tener dos números como sus campos, e incluso podrían tener el mismo Numbertipo y los mismos nombres, pero aún así no serían compatibles, y una instancia de PolarCoordinatesno sería un instancia de CartesianCoordinates. La capacidad de separar tipos por su propósito previsto y no por su implementación actual es una parte muy útil para escribir código más seguro y más fácil de mantener.

Kilian Foth
fuente
99
Cabe señalar que, en algunos idiomas, ser estructuralmente equivalente es suficiente para hacer que un tipo sea un subtipo del otro (y, a menudo, viceversa). Sin embargo, eso es decididamente poco común / impopular.
Telastyn
77
@Tim Un typedef no crea un tipo, alias el nombre utilizado para referirse a un tipo existente.
Doval
1
@DevSolar Mencionó explícitamente C, y aparte de C ++, no conozco ningún otro lenguaje que use esa palabra clave.
Doval
3
@Telastyn: esos idiomas se deben matar con fuego.
Jon Story
44
El subtipo estructural @JonStory es útil a nivel de módulo; la falta de esto es lo que te obliga a convertir todo interfaceen Java y C # si alguna vez quieres hacer pruebas unitarias. Terminas escribiendo una tonelada de repeticiones solo para poder cambiar la clase particular que usará tu programa aunque no tengas ninguna intención de cambiarlo en tiempo de ejecución.
Doval
6

Los tipos no son conjuntos.

Verá, la teoría de conjuntos tiene una serie de características que simplemente no se aplican a los tipos, y viceversa . Por ejemplo, un objeto tiene un solo tipo canónico. Puede ser una instancia de varios tipos diferentes, pero solo se usó uno de esos tipos para crear una instancia. La teoría de conjuntos no tiene noción de conjuntos "canónicos".

La teoría de conjuntos le permite crear subconjuntos sobre la marcha , si tiene una regla que describe lo que pertenece al subconjunto. La teoría de tipos generalmente no permite esto. Si bien la mayoría de los idiomas tienen un Numbertipo o algo similar, no tienen un EvenNumbertipo, ni sería sencillo crear uno. Quiero decir, es bastante fácil definir el tipo en sí, pero cualquier Numbers existente que sea incluso no se transformará mágicamente en EvenNumbers.

En realidad, decir que puedes "crear" subconjuntos es algo falso, porque los conjuntos son un tipo completamente diferente de animal. En la teoría de conjuntos, esos subconjuntos ya existen , en todas las infinitas formas en que puede definirlos. En la teoría de tipos, generalmente esperamos tratar con un número finito (si es grande) de tipos en un momento dado. Los únicos tipos que se dice que existen son los que hemos definido, no todos los tipos que podríamos definir.

Los conjuntos se no se les permite contener directa o indirectamente a sí mismos . Algunos lenguajes, como Python, proporcionan tipos con estructuras menos regulares (en Python, typeel tipo canónico es type, y objectse considera una instancia de object). Por otro lado, la mayoría de los idiomas no permiten que los tipos definidos por el usuario participen en este tipo de trucos.

Los conjuntos suelen superponerse sin estar contenidos entre sí. Esto es poco común en la teoría de tipos, aunque algunos lenguajes lo admiten en forma de herencia múltiple. Otros lenguajes, como Java, solo permiten una forma restringida de esto o no lo permiten por completo.

El tipo vacío existe (se llama el tipo inferior ), pero la mayoría de los idiomas no lo admiten o no lo consideran un tipo de primera clase. El "tipo que contiene todos los demás tipos" también existe (se llama el tipo superior ) y es ampliamente compatible, a diferencia de la teoría de conjuntos.

NB : Como algunos comentaristas señalaron anteriormente (antes de que el hilo se moviera al chat), es posible modelar tipos con teoría de conjuntos y otras construcciones matemáticas estándar. Por ejemplo, podría modelar la membresía de tipo como una relación en lugar de modelar tipos como conjuntos. Pero en la práctica, esto es mucho más simple si usa la teoría de categorías en lugar de la teoría de conjuntos. Así es como Haskell modela su teoría de tipos, por ejemplo.


La noción de "subtipo" es realmente muy diferente de la noción de "subconjunto". Si Xes un subtipo de Y, significa que podemos sustituir instancias de Yinstancias de Xy el programa seguirá "funcionando" en algún sentido. Esto es más conductual que estructural, aunque algunos lenguajes (por ejemplo, Go, Rust, posiblemente C) han elegido este último por razones de conveniencia, ya sea para el programador o la implementación del lenguaje.

Kevin
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Ingeniero mundial
4

Los tipos de datos algebraicos son la forma de discutir esto.

Hay tres formas fundamentales de combinar tipos:

  • Producto. Eso es básicamente lo que estás pensando:

    struct IntXDouble{
      int a; 
      double b;
    }
    

    es un tipo de producto; sus valores son todas las combinaciones posibles (es decir, tuplas) de uno inty uno double. Si considera los tipos de números como conjuntos, entonces la cardinalidad del tipo de producto es, de hecho, el producto de las cardinalidades de los campos.

  • Suma. En lenguajes de procedimiento, esto es un poco incómodo de expresar directamente (clásicamente se hace con uniones etiquetadas ), así que para una mejor comprensión, aquí hay un tipo de suma en Haskell:

    data IntOrDouble = AnInt Int
                     | ADouble Double
    

    los valores de este tipo tienen la forma AnInt 345, o ADouble 4.23, pero siempre hay solo un número involucrado (a diferencia del tipo de producto, donde cada valor tiene dos números). Entonces, la cardinalidad: primero enumera todos los Intvalores, cada uno debe combinarse con el AnIntconstructor. Además , todos los Doublevalores, cada uno combinado con ADouble. De ahí el tipo de suma .

  • Exponenciación 1 . No discutiré eso en detalle aquí porque no tiene una correspondencia OO clara en absoluto.

¿Y qué hay de las clases? Deliberadamente usé la palabra clave en structlugar de classpara IntXDouble. La cuestión es que una clase como tipo no se caracteriza realmente por sus campos, esos son simplemente detalles de implementación. El factor crucial es, más bien, qué valores distinguibles puede tener la clase.

Sin embargo, lo que es relevante es que el valor de una clase puede ser el valor de cualquiera de sus subclases . Por lo tanto, una clase es en realidad un tipo de suma en lugar de un tipo de producto: si Ay Bambos se derivan myClass, myClassesencialmente sería la suma de Ay B. Independientemente de la implementación real.


1 Esto es funciones (en el sentido matemático ); un tipo de función Int -> Doubleestá representado por el exponencial DoubleInt. Muy mal si su idioma no tiene las funciones adecuadas ...

a la izquierda
fuente
2
Lo siento, pero creo que esta es una respuesta muy pobre. Funciones hacer tener un análogo OO claro, a saber, los métodos (y los tipos de interfaz de un solo método). La definición básica de un objeto es que tiene tanto estado (campos / miembros de datos) como comportamiento (métodos / funciones de miembros); tu respuesta ignora lo último.
ruakh
@ruakh: no. Por supuesto, puede implementar funciones en OO, pero en general, los métodos no son funciones ( porque modifican el estado, etc.). Tampoco las "funciones" en lenguajes de procedimiento son funciones, de hecho. De hecho, las interfaces de método estático único se acercan más a los tipos de función / exponenciales, pero esperaba evitar la discusión de eso porque no tiene relevancia para esta pregunta.
Leftaroundabout
... que es más importante, mi anwer hace considerar el comportamiento. De hecho, el comportamiento suele ser la razón por la que usa la herencia, y la unificación de diferentes comportamientos posibles captura con precisión el aspecto de tipo suma de las clases OO.
Leftaroundabout
@ruakh Un método no puede sin su objeto. El análogo más cercano son los staticmétodos, excepto que todavía no son valores de primera clase. Lo que pasa con la mayoría de los lenguajes OO es que toman los objetos como el bloque de construcción más pequeño, por lo que si quieres algo más pequeño tienes que fingirlo con objetos, y aún así terminas arrastrando un montón de funciones semánticas que no deberían tener. Por ejemplo, no tiene sentido comparar funciones para la igualdad, pero aún puede comparar dos objetos de función falsa.
Doval
@Doval 1) puede pasar métodos alrededor de AFAIK, por lo que son valores de primera clase; 2) tiene sentido comparar funciones para la igualdad, la gente de JS lo hace todo el tiempo.
Den el
2

Lo siento, pero no sé acerca de la teoría "en bruto". Solo puedo proporcionar un enfoque práctico. Espero que esto sea aceptable en los programadores.SE; No estoy familiarizado con la etiqueta aquí.


Un tema central de OOP es la ocultación de información . Lo que los miembros de datos de una clase son, exactamente, no debería ser de interés para sus clientes. Un cliente envía mensajes a (llama métodos / funciones miembro de) una instancia, que podría o no modificar el estado interno. La idea es que las partes internas de una clase pueden cambiar, sin que el cliente se vea afectado por ella.

Un corrolario de esto es que la clase es responsable de garantizar que su representación interna siga siendo "válida". Supongamos una clase que almacena un número de teléfono (simplificado) en dos enteros:

    int areacode;
    int number;

Estos son los miembros de datos de la clase. Sin embargo, la clase probablemente será mucho más que solo sus miembros de datos, y ciertamente no se puede definir como "conjunto de todos los valores posibles de int x int". No debe tener acceso directo a los miembros de datos.

La construcción de una instancia podría negar cualquier número negativo. Quizás la construcción también normalizaría el código de área de alguna manera, o incluso verificaría el número entero. Por lo tanto, terminaría mucho más cerca de usted "(a,b) where a is an int and b is a double", porque ciertamente no hay dos int almacenados en esa clase.

Pero eso realmente no importa en lo que respecta a la clase. No es el tipo de miembros de datos, ni el rango de sus posibles valores lo que define la clase, son los métodos que se definen para ella.

Mientras esos métodos sigan siendo los mismos, el implementador podría cambiar los tipos de datos a coma flotante, BIGNUM, cadenas, lo que sea, y para todos los fines prácticos, seguiría siendo la misma clase .


Existen patrones de diseño para garantizar que tales cambios de representación interna se puedan realizar sin que el cliente se dé cuenta (por ejemplo, el modismo de pimpl en C ++, que oculta cualquier miembro de datos detrás de un puntero opaco ).

DevSolar
fuente
1
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.Los miembros de datos no definen una clase solo cuando los oculta. Ese puede ser el caso más común, pero ciertamente no se puede decir que sea cierto para todas las clases. Si incluso un solo campo es público, es tan importante como sus métodos.
Doval
1
A menos que esté codificando en Java, donde no tiene otra opción en el asunto e incluso sus tontos registros de imitación sin comportamiento deben ser class. (Marcarlos finalayuda a transmitir el punto, pero aún así). Sin embargo, todavía tiene un problema con los protectedmiembros, que se pueden heredar y, por lo tanto, forman parte de una segunda API para implementadores de subclases.
Doval
1
@Doval: entendí que se trataba de una pregunta de "teoría", por lo que me mantuve tan alejado de los problemas reales del lenguaje como me fue posible. (Al igual que me mantengo lo más alejado posible de Java y protecteden la práctica. ;-))
DevSolar
3
El problema es que a classes una construcción dependiente del lenguaje. Hasta donde sé, no existe una classteoría de tipo.
Doval
1
@Doval: ¿No significa eso que la teoría de tipos per se no se aplica a las clases, ya que son una construcción fuera del alcance de esa teoría?
DevSolar
2
  • Un tipo es una descripción de una categoría / rango de valores, estructuras compuestas o lo que tiene. OOPwise, es similar a una "interfaz". (En el sentido agnóstico del lenguaje. El sentido específico del lenguaje, no tanto. En Java, por ejemplo, intes un tipo , pero no tiene relación con un interface. Las especificaciones de campo público / protegido, tampoco son parte de un interface, pero son parte de una "interfaz" o tipo ).

    El punto principal es que es mucho más una definición semántica que concreta. La estructura solo tiene en cuenta que los campos / comportamientos expuestos y sus propósitos definidos se alinean. Si no tiene ambos, no tiene compatibilidad de tipos.

  • Una clase es la realización de un tipo. Es una plantilla que realmente define la estructura interna, el comportamiento adjunto, etc.

cHao
fuente