Vés al contingut

Subtipatge

De la Viquipèdia, l'enciclopèdia lliure

En teoria de llenguatges de programació, el subtipatge (també anomenat polimorfisme de subtipus o polimorfisme d'inclusió) és una forma de polimorfisme de tipus. Un subtipus és un tipus de dades que està relacionat amb un altre tipus de dades (el supertipus) per alguna noció de substituïbilitat, és a dir, que els elements del programa (normalment subrutines o funcions), escrits per operar sobre elements del supertipus, també poden operar sobre elements del subtipus.[1]

Si S és un subtipus de T, la relació de subtipa (escrita com a S <: T , S ⊑ T, o S ≤: T) significa que qualsevol terme de tipus S es pot utilitzar amb seguretat en qualsevol context on s'esperi un terme de tipus T. La semàntica precisa del subtipat aquí depèn crucialment de les particularitats de com "s'ha d'utilitzar amb seguretat" i "qualsevol context" es defineixen mitjançant un formalisme de tipus o llenguatge de programació determinat. El sistema de tipus d'un llenguatge de programació defineix essencialment la seva pròpia relació de subtipat, que pot ser trivial, si el llenguatge no admet (o admet molt pocs) mecanismes de conversió.[2]

A causa de la relació de subtipificació, un terme pot pertànyer a més d'un tipus. Per tant, la subtipificació és una forma de polimorfisme de tipus. En la programació orientada a objectes, el terme "polimorfisme" s'utilitza habitualment per referir-se únicament a aquest polimorfisme de subtipus, mentre que les tècniques de polimorfisme paramètric es considerarien programació genèrica.[3]

El concepte de subtipificació està relacionat amb les nocions lingüístiques d'hiponímia i holonímia. També està relacionat amb el concepte de quantificació limitada en lògica matemàtica (vegeu Lògica ordenada per ordre). La subtipificació no s'ha de confondre amb la noció d'herència (de classe o d'objecte) dels llenguatges orientats a objectes; la subtipificació és una relació entre tipus (interfícies en argot orientat a objectes), mentre que l'herència és una relació entre implementacions derivades d'una característica del llenguatge que permet crear nous objectes a partir dels existents. En diversos llenguatges orientats a objectes, la subtipificació s'anomena herència d'interfícies, i l'herència s'anomena herència d'implementació.[4]

Orígens

[modifica]

La noció de subtipificació en els llenguatges de programació data de la dècada del 1960; es va introduir en els derivats de Simula. Els primers tractaments formals de la subtipificació van ser donats per John C. Reynolds el 1980, que va utilitzar la teoria de categories per formalitzar les conversions implícites, i Luca Cardelli (1985).

El concepte de subtipificació ha guanyat visibilitat (i sinonímia amb polimorfisme en alguns cercles) amb l'adopció generalitzada de la programació orientada a objectes. En aquest context, el principi de substitució segura sovint s'anomena principi de substitució de Liskov, en honor a Barbara Liskov, que el va popularitzar en una conferència sobre programació orientada a objectes el 1987. Com que ha de considerar objectes mutables, la noció ideal de subtipificació definida per Liskov i Jeannette Wing, anomenada subtipificació comportamental, és considerablement més forta que la que es pot implementar en un verificador de tipus. (Vegeu § Function types a continuació per a més detalls.)

Exemples

[modifica]
Image
Exemple de subtipus: on bird és el supertipus i tots els altres són subtipus, tal com s'indica amb la fletxa en la notació UML.

Un exemple pràctic i senzill de subtipus es mostra al diagrama. El tipus "ocell" té tres subtipus: "ànec", "cucut" i "estruç". Conceptualment, cadascun d'aquests és una varietat del tipus bàsic "ocell" que hereta moltes característiques d'"ocell", però té algunes diferències específiques. En aquest diagrama s'utilitza la notació UML, amb fletxes amb el cap obert que mostren la direcció i el tipus de relació entre el supertipus i els seus subtipus.

Com a exemple més pràctic, un llenguatge podria permetre l'ús de valors enters allà on s'esperin valors de coma flotant ( Integer <: Float ), o podria definir un tipus genèric Number com un supertipus comú d'enters i reals. En aquest segon cas, només tenim Integer <: Number i Float <: Number, però Integer i Float no són subtipus l'un de l'altre.

Els programadors poden aprofitar el subtipat per escriure codi d'una manera més abstracta del que seria possible sense ell. Considereu l'exemple següent:

function max (x as Number, y as Number) is
 if x < y then
 return y
 else
 return x
end

Si tant un nombre enter com un nombre real són subtipus de Number, i es defineix un operador de comparació amb un nombre arbitrari per a tots dos tipus, aleshores es poden passar valors de qualsevol tipus a aquesta funció. Tanmateix, la possibilitat mateixa d'implementar aquest operador restringeix molt el tipus Number (per exemple, no es pot comparar un nombre enter amb un nombre complex), i en realitat només té sentit comparar nombres enters amb nombres enters i nombres reals amb nombres reals. Reescriure aquesta funció perquè només accepti 'x' i 'y' del mateix tipus requereix un polimorfisme limitat.

El subtipat permet que un tipus determinat sigui substituït per un altre tipus o abstracció. Es diu que el subtipat estableix una relació és-una entre el subtipus i alguna abstracció existent, ja sigui implícitament o explícitament, depenent del suport del llenguatge. La relació es pot expressar explícitament mitjançant l'herència en llenguatges que admeten l'herència com a mecanisme de subtipat.

El següent codi C++ estableix una relació d'herència explícita entre les classes B i A, on B és tant una subclasse com un subtipus d' A, i es pot utilitzar com a A sempre que s'especifiqui una B (mitjançant una referència, un punter o el mateix objecte).

class A
{ public:
 void DoSomethingALike() const {}
};

class B : public A
{ public:
 void DoSomethingBLike() const {}
};

void UseAnA(A const& some_A)
{
 some_A.DoSomethingALike();
}

void SomeFunc()
{
 B b;
 UseAnA(b); // b can be substituted for an A.
}

Python

[modifica]

El següent codi Python estableix una relació d'herència explícita entre les classes B i A, on B és tant una subclasse com un subtipus d'A, i es pot utilitzar com a A sempre que es requereixi un B

class A:
 def do_something_a_like(self):
 pass

class B(A):
 def do_something_b_like(self):
 pass

def use_an_a(some_a):
 some_a.do_something_a_like()

def some_func():
 b = B()
 use_an_a(b) # b can be substituted for an A.

En el següent exemple, type(a) és un tipus "regular" i type(type(a)) és un metatipus. Tot i que, tal com estan distribuïts, tots els tipus tenen el mateix metatipus ( PyType_Type, que també és el seu propi metatipus), això no és un requisit. El tipus de les classes clàssiques, conegut com a types.ClassType, també es pot considerar types.ClassType metatipus diferent.[5]

>>> a = 0
>>> type(a)
<type 'int'>
>>> type(type(a))
<type 'type'>
>>> type(type(type(a)))
<type 'type'>
>>> type(type(type(type(a))))
<type 'type'>

Java

[modifica]

A Java, la relació entre els paràmetres de tipus d'una classe o interfície i els paràmetres de tipus d'una altra es determinen mitjançant les clàusules extends i implements.

Usant les classes Collections, ArrayList<E> implementa List<E> i List<E> estén Collection<E>. Per tant, ArrayList<String> és un subtipus de List<String>, que és un subtipus de Collection<String>. La relació de subtipus es conserva entre els tipus automàticament. Quan es defineix una interfície, PayloadList, que associa un valor opcional de tipus genèric P amb cada element, la seva declaració podria ser així:

interface PayloadList<E, P> extends List<E> {
 void setPayload(int index, P val);
 ...
}

Les següents parametritzacions de PayloadList són subtipus de List<String> :

PayloadList<String, String>
PayloadList<String, Integer>
PayloadList<String, Exception>

Referències

[modifica]
  1. ↑ «Sub: Subtyping» (en anglès). [Consulta: 2 setembre 2025].
  2. ↑ «[https://groups.seas.harvard.edu/courses/cs153/2019fa/lectures/Lec15-Subtyping.pdfc CS153: Compilers Lecture 15: Subtyping]» (en anglès). [Consulta: 2 setembre 2025].
  3. ↑ «Subtyping and Variance - The Rustonomicon» (en anglès). [Consulta: 2 setembre 2025].
  4. ↑ Liskov, Barbara H.; Wing, Jeannette M. «A behavioral notion of subtyping». ACM Trans. Program. Lang. Syst., 16, 6, 01-11-1994, p. 1811–1841. DOI: 10.1145/197320.197383. ISSN: 0164-0925.
  5. ↑ Guido van Rossum. «Subtyping Built-in Types» (en anglès). [Consulta: 2 octubre 2012].