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)
dondea
es un int yb
es un doble", o - "{
(x,y)
|x
es cualquier int,y
es cualquier doble}"?
¿Qué significa una instancia de myclass
?
- "
(a,b)
dondea
es un int yb
es 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óndex
hay algún int yy
cualquier doble?
a
yb
son miembros de ese tipo, como menciona Killian Forth. Myclass es isomorfo a registros con camposa
yb
de tipoint
ydouble
- podría tomar un registro como ese y convertirlo en una instancia demyclass
.Respuestas:
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
CartesianCoordinates
y unaPolarCordinates
clase, ambos podrían tener dos números como sus campos, e incluso podrían tener el mismoNumber
tipo y los mismos nombres, pero aún así no serían compatibles, y una instancia dePolarCoordinates
no sería un instancia deCartesianCoordinates
. 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.fuente
interface
en 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.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
Number
tipo o algo similar, no tienen unEvenNumber
tipo, ni sería sencillo crear uno. Quiero decir, es bastante fácil definir el tipo en sí, pero cualquierNumber
s existente que sea incluso no se transformará mágicamente enEvenNumber
s.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,
type
el tipo canónico estype
, yobject
se considera una instancia deobject
). 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
X
es un subtipo deY
, significa que podemos sustituir instancias deY
instancias deX
y 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.fuente
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:
es un tipo de producto; sus valores son todas las combinaciones posibles (es decir, tuplas) de uno
int
y unodouble
. 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:
los valores de este tipo tienen la forma
AnInt 345
, oADouble 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 losInt
valores, cada uno debe combinarse con elAnInt
constructor. Además , todos losDouble
valores, cada uno combinado conADouble
. 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
struct
lugar declass
paraIntXDouble
. 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
A
yB
ambos se derivanmyClass
,myClass
esencialmente sería la suma deA
yB
. Independientemente de la implementación real.1 Esto es funciones (en el sentido matemático ); un tipo de función
Int -> Double
está representado por el exponencialDouble
Int
. Muy mal si su idioma no tiene las funciones adecuadas ...fuente
static
mé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.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:
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 ).
fuente
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.class
. (Marcarlosfinal
ayuda a transmitir el punto, pero aún así). Sin embargo, todavía tiene un problema con losprotected
miembros, que se pueden heredar y, por lo tanto, forman parte de una segunda API para implementadores de subclases.protected
en la práctica. ;-))class
es una construcción dependiente del lenguaje. Hasta donde sé, no existe unaclass
teoría de tipo.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,
int
es un tipo , pero no tiene relación con uninterface
. Las especificaciones de campo público / protegido, tampoco son parte de uninterface
, 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.
fuente