En la teoría de lenguajes de programación y la teoría de tipos , el polimorfismo permite que un valor o variable tenga más de un tipo y permite que una operación determinada se realice sobre valores de más de un tipo. [ 1 ]
En la programación orientada a objetos , el polimorfismo es la provisión de una interfaz para entidades de diferentes tipos de datos . [ 2 ] El concepto se toma prestado de un principio de la biología según el cual un organismo o especie puede tener muchas formas o etapas diferentes. [ 3 ]
Las formas principales de polimorfismo más comúnmente reconocidas son:
- Polimorfismo ad hoc : define una interfaz común para un conjunto arbitrario de tipos especificados individualmente.
- Polimorfismo paramétrico : no especifica tipos concretos y, en su lugar, utiliza símbolos abstractos que pueden sustituir a cualquier tipo.
- Subtipado (también llamado polimorfismo de subtipo o polimorfismo de inclusión ): cuando un nombre denota instancias de muchas clases diferentes relacionadas por una superclase común. [ 4 ]
Historia
El interés por los sistemas de tipos polimórficos se desarrolló significativamente en la década de 1990, y las implementaciones prácticas comenzaron a aparecer hacia finales de la década. El polimorfismo ad hoc y el polimorfismo paramétrico fueron descritos originalmente en el libro de Christopher Strachey , *Fundamental Concepts in Programming Languages* , [ 5 ] donde se enumeran como "las dos clases principales" de polimorfismo. El polimorfismo ad hoc era una característica de ALGOL 68 , mientras que el polimorfismo paramétrico era la característica central del sistema de tipos de ML .
En un artículo de 1985, Peter Wegner y Luca Cardelli introdujeron el término polimorfismo de inclusión para modelar subtipos y herencia , [ 1 ] citando a Simula como el primer lenguaje de programación en implementarlo.
Formularios
polimorfismo ad hoc
Christopher Strachey eligió el término polimorfismo ad hoc para referirse a funciones polimórficas que pueden aplicarse a argumentos de diferentes tipos, pero que se comportan de manera diferente según el tipo del argumento al que se aplican (también conocido como sobrecarga de funciones o sobrecarga de operadores ). [ 5 ] Aquí, "ad hoc" significa que esta forma de polimorfismo no es una característica fundamental del sistema de tipos. En el ejemplo de Javaadd que se muestra a continuación, las funciones parecen funcionar genéricamente sobre dos tipos ( entero y cadena ) al observar las invocaciones, pero el compilador las considera funciones completamente distintas :
clase AdHocPolymorphic { public String add ( int x , int y ) { return String . format ( "Suma: %d" , x + y ); }public String add ( String name ) { return String . format ( "Añadido " , name ); } }public class Adhoc { public static void main ( String [] args ) { AdHocPolymorphic poly = new AdHocPolymorphic ();System.out.println ( poly.add ( 1 , 2 ) ); // imprime " Suma: 3" System.out.println ( poly.add ( " Jay " ) ) ; // imprime " Jay añadido " } }En los lenguajes de tipado dinámico, la situación puede ser más compleja, ya que la función correcta que debe invocarse puede determinarse únicamente en tiempo de ejecución.
La conversión de tipo implícita también se ha definido como una forma de polimorfismo, denominada "polimorfismo de coerción". [ 1 ] [ 6 ]
Polimorfismo paramétrico
El polimorfismo paramétrico permite escribir una función o un tipo de dato de forma genérica, de modo que pueda manejar valores de manera uniforme sin depender de su tipo. [ 7 ] El polimorfismo paramétrico es una forma de hacer que un lenguaje sea más expresivo manteniendo al mismo tiempo una seguridad de tipos estática completa .
El concepto de polimorfismo paramétrico se aplica tanto a los tipos de datos como a las funciones . Una función que puede evaluarse o aplicarse a valores de diferentes tipos se conoce como función polimórfica. Un tipo de dato que puede parecer de un tipo generalizado (por ejemplo, una lista con elementos de tipo arbitrario) se denomina tipo de dato polimórfico, al igual que el tipo generalizado a partir del cual se realizan dichas especializaciones.
El polimorfismo paramétrico es omnipresente en la programación funcional, donde a menudo se le denomina simplemente "polimorfismo". El siguiente ejemplo en Haskell muestra un tipo de dato de lista parametrizada y dos funciones paramétricamente polimórficas sobre ella:
Lista de datos a = Nil | Cons a ( Lista a )longitud :: Lista a -> Longitud entera Nil = 0 longitud ( Cons x xs ) = 1 + longitud xsmapa :: ( a -> b ) -> Lista a -> Lista b mapa f Nil = Nil mapa f ( Cons x xs ) = Cons ( f x ) ( mapa f xs )El polimorfismo paramétrico también está disponible en varios lenguajes orientados a objetos. Por ejemplo, las plantillas en C++ y D , o bajo el nombre de genéricos en C# , Delphi , Java y Go :
clase Lista < T > { clase Nodo < T > { T elem ; Nodo < T > siguiente ; } Nodo < T > cabeza ; int longitud () { ... } }Lista < B > map ( Func < A , B > f , Lista < A > xs ) { ... }John C. Reynolds (y posteriormente Jean-Yves Girard ) desarrollaron formalmente esta noción de polimorfismo como una extensión del cálculo lambda (denominado cálculo lambda polimórfico o Sistema F ). Cualquier función paramétricamente polimórfica está necesariamente restringida en lo que puede hacer, ya que trabaja sobre la forma de los datos en lugar de su valor, lo que da lugar al concepto de parametricidad .
Subtipificación
Algunos lenguajes emplean el concepto de subtipo (también llamado polimorfismo de subtipo o polimorfismo de inclusión ) para restringir el rango de tipos que se pueden usar en un caso particular de polimorfismo. En estos lenguajes, el subtipo permite escribir una función que acepte un objeto de un tipo T determinado , pero que también funcione correctamente si se le pasa un objeto de un tipo S que es un subtipo de T (según el principio de sustitución de Liskov ). Esta relación de tipos se escribe a veces como S <: T. Por el contrario, se dice que T es un supertipo de S , escrito como T :> S. El polimorfismo de subtipo generalmente se resuelve dinámicamente (véase más adelante).
En el siguiente ejemplo de Java, los gatos y los perros se convierten en subtipos de mascotas. El procedimiento letsHear()acepta una mascota, pero también funcionará correctamente si se le pasa un subtipo:
clase abstracta Pet { cadena abstracta habla (); }clase Gato extiende Mascota { String habla () { return "¡Miau!" ; } }clase Perro extiende Mascota { String habla () { return "¡Guau!" ; } }static void letsHear ( final Pet pet ) { System.out.println ( pet.speak ( ) ) ; }static void main ( String [] args ) { letsHear ( new Cat ()); letsHear ( new Dog ()); }![]()
En otro ejemplo, si Number , Rational e Integer son tipos tales que Number :> Rational y Number :> Integer ( Rational e Integer como subtipos de un tipo Number que es un supertipo de ellos), una función escrita para tomar un Number funcionará igual de bien cuando se le pase un Integer o un Rational que cuando se le pase un Number . El tipo real del objeto puede ocultarse a los clientes en una caja negra y accederse a través de la identidad del objeto . Si el tipo Number es abstracto , puede que no sea posible acceder a un objeto cuyo tipo más derivado sea Number (ver tipo de datos abstracto , clase abstracta ). Este tipo particular de jerarquía de tipos se conoce, especialmente en el contexto del lenguaje Scheme , como una torre numérica , y generalmente contiene muchos más tipos.
Los lenguajes de programación orientados a objetos ofrecen polimorfismo de subtipos mediante la herencia (también conocida como subclase ). En las implementaciones típicas, cada clase contiene lo que se denomina una tabla virtual (abreviadamente llamada vtable ), una tabla de funciones que implementan la parte polimórfica de la interfaz de la clase, y cada objeto contiene un puntero a la vtable de su clase, que se consulta cada vez que se llama a un método polimórfico. Este mecanismo es un ejemplo de:
- enlace tardío , porque las llamadas a funciones virtuales no se enlazan hasta el momento de la invocación;
- Despacho único (es decir, polimorfismo de un solo argumento), porque las llamadas a funciones virtuales se vinculan simplemente buscando en la tabla virtual proporcionada por el primer argumento (el
thisobjeto), por lo que los tipos en tiempo de ejecución de los demás argumentos son completamente irrelevantes.
Lo mismo ocurre con la mayoría de los demás sistemas de objetos populares. Sin embargo, algunos, como Common Lisp Object System , ofrecen despacho múltiple , bajo el cual las llamadas a métodos son polimórficas en todos los argumentos.
La interacción entre el polimorfismo paramétrico y la subtipificación conduce a los conceptos de varianza de tipo y cuantificación acotada .
Polimorfismo de filas
El polimorfismo de filas [ 8 ] es un concepto similar, pero distinto, al subtipado. Se ocupa de los tipos estructurales . Permite el uso de todos los valores cuyos tipos tienen ciertas propiedades, sin perder la información de tipo restante.
Politipismo
Un concepto relacionado es el politipismo (o genericidad de tipos de datos ). Una función politípica es más general que una polimórfica, y en dicha función, "aunque se pueden proporcionar casos fijos ad hoc para tipos de datos específicos, no existe un combinador ad hoc". [ 9 ]
polimorfismo de rango
El polimorfismo de rango es una de las características definitorias de los lenguajes de programación de matrices , como APL . La esencia del modelo de programación polimórfica de rango es tratar implícitamente todas las operaciones como operaciones de agregación, utilizables en matrices con un número arbitrario de dimensiones, [ 10 ] lo que significa que el polimorfismo de rango permite definir funciones para operar en matrices de cualquier forma y tamaño.
Aspectos de implementación
Polimorfismo estático y dinámico
El polimorfismo se puede distinguir según el momento en que se selecciona la implementación: estáticamente (en tiempo de compilación) o dinámicamente (en tiempo de ejecución, normalmente mediante una función virtual ). Esto se conoce respectivamente como despacho estático y despacho dinámico , y las formas correspondientes de polimorfismo se denominan, en consecuencia, polimorfismo estático y polimorfismo dinámico .
El polimorfismo estático se ejecuta más rápido, ya que no hay sobrecarga de despacho dinámico, pero requiere soporte adicional del compilador. Además, el polimorfismo estático permite un análisis estático más profundo por parte de los compiladores (especialmente para la optimización), las herramientas de análisis de código fuente y los programadores. El polimorfismo dinámico es más flexible, pero más lento; por ejemplo, permite el tipado dinámico ( duck typing ) y una biblioteca de enlace dinámico puede operar con objetos sin conocer su tipo completo.
El polimorfismo estático suele darse en el polimorfismo ad hoc y el polimorfismo paramétrico, mientras que el polimorfismo dinámico es habitual en el polimorfismo de subtipos. Sin embargo, es posible lograr el polimorfismo estático con subtipado mediante un uso más sofisticado de la metaprogramación de plantillas , concretamente el patrón de plantilla curiosamente recurrente .
Cuando el polimorfismo se expone a través de una biblioteca , el polimorfismo estático se vuelve imposible para las bibliotecas dinámicas, ya que no hay forma de saber qué tipos tienen los parámetros cuando se construye el objeto compartido . Mientras que lenguajes como C++ y Rust utilizan plantillas monomorfizadas , el lenguaje de programación Swift hace un uso extensivo del despacho dinámico para construir la interfaz binaria de la aplicación para estas bibliotecas por defecto. Como resultado, se puede compartir más código para reducir el tamaño del sistema a costa de una sobrecarga en tiempo de ejecución. [ 11 ]
Véase también
Referencias
- 1 2 3 Cardelli, Luca ; Wegner, Peter (diciembre de 1985). "Sobre la comprensión de los tipos, la abstracción de datos y el polimorfismo" ( PDF) . ACM Computing Surveys . 17 (4): 471– 523. CiteSeerX 10.1.1.117.695 . doi : 10.1145/6041.6042 . S2CID 2921816.
Los lenguajes tipados convencionales, como Pascal, se basan en la idea de que las funciones y los procedimientos, y por lo tanto sus operandos, tienen un tipo único. Se dice que estos lenguajes son
monomórficos
, en el sentido de que cada valor y variable puede interpretarse como de un solo tipo. Los lenguajes de programación monomórficos pueden contrastarse con los lenguajes
polimórficos
en los que algunos valores y variables pueden tener más de un tipo. Las funciones polimórficas son funciones cuyos operandos (parámetros reales) pueden tener más de un tipo. Los tipos polimórficos son aquellos cuyas operaciones son aplicables a valores de más de un tipo.
- ↑ Stroustrup, Bjarne (19 de febrero de 2007). "Glosario de C++ de Bjarne Stroustrup" .
Polimorfismo: proporcionar una única interfaz a entidades de diferentes tipos.
- ↑ "Polimorfismo" . Los tutoriales de Java: Aprendiendo el lenguaje Java: Interfaces y herencia . Oracle . Consultado el 8 de septiembre de 2021 .
- ↑ Conallen, J.; Engle, M.; Houston, K.; Maksimchuk, R.; Young, B.; Booch, G. (2007). Análisis y diseño orientado a objetos con aplicaciones (3.ª ed.). Pearson Education. ISBN 9780132797443.
- 1 2 Strachey, Christopher (2000). "Conceptos fundamentales en lenguajes de programación". Higher-Order and Symbolic Computation . 13 (1/2): 11– 49. CiteSeerX 10.1.1.332.3161 . doi : 10.1023/A:1010000313106 . ISSN 1573-0557 . S2CID 14124601 .
- ↑ Tucker, Allen B. (2004). Manual de informática (2.ª ed.). Taylor & Francis. págs. 91–. ISBN 978-1-58488-360-9.
- ↑ Pierce, BC (2002). "23.2 Variedades de polimorfismo" . Tipos y lenguajes de programación . MIT Press. págs. 340–1 . ISBN 9780262162098.
- ↑ Wand, Mitchell (junio de 1989). "Inferencia de tipos para concatenación de registros y herencia múltiple". Actas del Cuarto Simposio Anual sobre Lógica en Ciencias de la Computación . págs. 92–97 . doi : 10.1109/LICS.1989.39162 .
- ↑ Lämmel, Ralf; Visser, Joost (2002). «Combinadores tipados para recorridos genéricos». Aspectos prácticos de los lenguajes declarativos: 4.º Simposio Internacional . Springer. págs. 137–154 , véase pág. 153. CiteSeerX 10.1.1.18.5727 . ISBN 354043092X.
- ↑ Slepak, Justin; Shivers, Olin; Manolios, Panagiotis (2019). "La semántica del polimorfismo de rango". arXiv : 1907.00509 [ cs.PL ].
- ↑ Beingessner, Alexis. "Cómo Swift logró el enlace dinámico donde Rust no pudo" .
Enlaces externos
- Ejemplos de polimorfismo en C++
- Objetos y polimorfismo (Prólogo visual)
- Polimorfismo en MSDN
- Documentación de polimorfismo en Java sobre Oracle
- Polimorfismo (informática)
- Tipos de datos
- Programación funcional
- Programación orientada a objetos
- conceptos de lenguajes de programación
- teoría de tipos
- Programación genérica
- Comparación de lenguajes de programación
