Un ejemplo simple de subtipado es la relación entre dos tipos, "perro" y "animal", donde animal es el supertipo y perro el subtipo. Un perro es un animal, pero no todos los animales son perros.
Como ejemplo de subtipado en un lenguaje orientado a objetos:
// archivo Animal.java
interface Animal {
public void grito();
}
// archivo perro.java
class Perro implements Animal {
public void grito() {
this.ladrido();
}
public void ladrido() {
System.out.println("Guau!")
}
public Perro() { }
// ...
}
// archivo main.java
class Main {
public static int main(int argc, String [] argv) {
Animal animal = Perro()
animal.grito()
// -> Guau!
}
}
Como ejemplo de subtipado por duck typing:
class Perro:
def grito(self):
print("Guau!")
class Pato:
def grito(self):
print("Cuac!")
def haz_ruido(animal):
# En este caso, animal es un supertipo de todos los valores con una función
# grito()
animal.grito()
perro = Perro()
haz_ruido(perro)
# -> Guau!
pato = Pato()
haz_ruido(pato)
# -> Cuac!
Es típico en lenguajes funcionales incluir un sistema de subtipado de registros de forma que S es un subtipo de T si todas las claves del supertipo T están presentes en el subtipo S con valores de tipos compatibles.
Esta definición obedece al hecho de que siempre se pueden ignorar claves adicionales de S para compatibilizarlos y que la reordenación de pares clave-valor es legítima, puesto que no existe el acceso indexado a esta estructura de datos, solo por clave.[1]
De esta forma, si en una variante del cálculo lambda tipado y con soporte para declaraciones de tipos, el estilo de subtipado mencionado y registros y enteros, se define la siguiente expresión:
T := {a: Entero, b: Entero}
S := {b: Entero, c: Entero, a: Entero}
(λv: T. (T.a)) ({a = 3, b = 2, c = 5}: S)
Esta sería tipable y de tipo 'Entero'.