stringtranslate.com

Polimorfismo (informática)

En la teoría de lenguajes de programación y teoría de tipos , el polimorfismo es el uso de un símbolo para representar múltiples tipos diferentes. [1]

En la programación orientada a objetos , el polimorfismo es la provisión de una interfaz a entidades de diferentes tipos de datos . [2] El concepto se toma prestado de un principio en biología donde un organismo o especie puede tener muchas formas o etapas diferentes. [3]

Las formas principales de polimorfismo más comúnmente reconocidas son:

Historia

El interés en los sistemas de tipos polimórficos se desarrolló significativamente en la década de 1990, y las implementaciones prácticas comenzaron a aparecer hacia fines de la década. El polimorfismo ad hoc y el polimorfismo paramétrico se describieron originalmente en Conceptos fundamentales en lenguajes de programación de Christopher Strachey , [5] donde se los enumera como "las dos clases principales" de polimorfismo. El polimorfismo ad hoc fue una característica de ALGOL 68 , mientras que el polimorfismo paramétrico fue la característica principal 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 se pueden aplicar a argumentos de diferentes tipos, pero que se comportan de manera diferente según el tipo de argumento al que se aplican (también conocido como sobrecarga de funciones o sobrecarga de operadores ). [5] El término " ad hoc " en este contexto no es peyorativo: en cambio, significa que esta forma de polimorfismo no es una característica fundamental del sistema de tipos. En el ejemplo de JavaAdd a continuación, las funciones parecen funcionar de manera genérica sobre dos tipos ( entero y cadena ) al observar las invocaciones, pero el compilador las considera dos funciones completamente distintas para todos los efectos:

clase  AdHocPolymorphic { público String add ( int x , int y ) { devolver "Suma: " + ( x + y ); }            public String add ( String nombre ) { return "Agregado " + nombre ; } }       clase pública adhoc { pública estática void main ( String [] args ) { AdHocPolymorphic poly = new AdHocPolymorphic ();               Sistema . out . println ( poly . add ( 1 , 2 ) ); // imprime "Suma: 3" Sistema . out . println ( poly . add ( "Jay" ) ); // imprime "Se agregó Jay" } }        

En lenguajes tipados dinámicamente la situación puede ser más compleja, ya que la función correcta que debe invocarse podría determinarse solo en tiempo de ejecución.

La conversión de tipos 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 datos 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 y al mismo tiempo mantener la seguridad de tipos estáticos completa .

El concepto de polimorfismo paramétrico se aplica tanto a tipos de datos como a funciones . Una función que puede evaluarse o aplicarse a valores de diferentes tipos se conoce como función polimórfica. Un tipo de datos que puede parecer de un tipo generalizado (por ejemplo, una lista con elementos de tipo arbitrario) se denomina tipo de datos 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 lo denomina simplemente "polimorfismo". El siguiente ejemplo en Haskell muestra un tipo de datos de lista parametrizada y dos funciones polimórficas paramétricas sobre ellos:

datos Lista a = Nil | Cons a ( Lista a )         longitud :: Lista a -> Longitud entera Nil = 0 longitud ( Cons x xs ) = 1 + longitud xs                mapa :: ( 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 length () { ... } }                 Lista < B > mapa ( Func < A , B > f , Lista < A > xs ) { ... }       

John C. Reynolds (y más tarde Jean-Yves Girard ) desarrollaron formalmente esta noción de polimorfismo como una extensión del cálculo lambda (llamado cálculo lambda polimórfico o Sistema F ). Cualquier función paramétricamente polimórfica está necesariamente restringida en lo que puede hacer, trabajando sobre la forma de los datos en lugar de su valor, lo que conduce al concepto de parametricidad .

Subtipificación

Algunos lenguajes emplean la idea de subtipificación (también llamada 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, la subtipificación permite que una función se escriba para tomar un objeto de un cierto tipo T , pero también funcione correctamente, si se le pasa un objeto que pertenece a un tipo S que es un subtipo de T (de acuerdo con el principio de sustitución de Liskov ). Esta relación de tipo a veces se escribe S <: T. Por el contrario, se dice que T es un supertipo de S , escrito T  :> S. El polimorfismo de subtipo generalmente se resuelve dinámicamente (ver a continuación).

En el siguiente ejemplo de Java, los gatos y los perros se convierten en subtipos de las mascotas. El procedimiento letsHear()acepta una mascota, pero también funcionará correctamente si se le pasa un subtipo:

clase abstracta Mascota { cadena abstracta hablar (); }      clase  Gato extiende Mascota { String hablar () { devolver "¡Miau!" ; } }         clase  Perro extiende Mascota { String hablar () { devolver "¡Guau!" ; } }         void estático letsHear ( final Pet pet ) { println ( pet.speak ( ) ) ; }      void estático principal ( String [] args ) { letsHear ( nuevo Gato ()); letsHear ( nuevo Perro ()); }        

En otro ejemplo, si Number , Rational y Integer son tipos tales que Number  :> Rational y Number  :> Integer ( Rational y Integer como subtipos de un tipo Number que es un supertipo de ellos), una función escrita para tomar un Number funcionará igualmente bien cuando se le pase un Integer o Rational que cuando se le pase un Number . El tipo real del objeto se puede ocultar a los clientes en una caja negra y acceder a él a través de object identity . Si el tipo Number es abstract , puede que ni siquiera sea posible conseguir un objeto cuyo tipo más derivado sea Number (consulte 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 normalmente contiene muchos más tipos.

Los lenguajes de programación orientados a objetos ofrecen polimorfismo de subtipos mediante subclasificación (también conocida como herencia ). En implementaciones típicas, cada clase contiene lo que se denomina una tabla virtual (abreviada como 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 luego se consulta cada vez que se llama a un método polimórfico. Este mecanismo es un ejemplo de:

Lo mismo ocurre con la mayoría de los demás sistemas de objetos populares. Sin embargo, algunos, como Common Lisp Object System , ofrecen varios envíos , en los que 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 y cuantificación acotada .

Polimorfismo de filas

El polimorfismo de filas [8] es un concepto similar, pero distinto del subtipado. Se ocupa de los tipos estructurales . Permite el uso de todos los valores cuyos tipos tienen determinadas 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 una función de este tipo, "aunque se pueden proporcionar casos ad hoc fijos 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 agregadas, utilizables en matrices con un número arbitrario de dimensiones, [10] lo que significa que el polimorfismo de rango permite que se definan 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 a través de una función virtual ). Esto se conoce respectivamente como envío estático y envío 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, porque no hay una sobrecarga de despacho dinámico, pero requiere soporte adicional del compilador. Además, el polimorfismo estático permite un mayor análisis estático por parte de los compiladores (especialmente para la optimización), las herramientas de análisis de código fuente y los lectores humanos (programadores). El polimorfismo dinámico es más flexible pero más lento; por ejemplo, el polimorfismo dinámico permite el tipado de pato y una biblioteca vinculada dinámicamente puede operar en objetos sin conocer su tipo completo.

El polimorfismo estático se da normalmente en el polimorfismo ad hoc y en el polimorfismo paramétrico, mientras que el polimorfismo dinámico es habitual en el polimorfismo de subtipos. Sin embargo, es posible lograr un polimorfismo estático con subtipificación 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 son los parámetros cuando se crea el objeto compartido . Mientras que los lenguajes como C++ y Rust utilizan plantillas monomorfizadas , el lenguaje de programación Swift hace un uso extensivo del envío dinámico para construir la interfaz binaria de la aplicación para estas bibliotecas de forma predeterminada. Como resultado, se puede compartir más código para un tamaño de sistema reducido a costa de la sobrecarga de tiempo de ejecución. [11]

Véase también

Referencias

  1. ^ abc Cardelli, Luca ; Wegner, Peter (diciembre de 1985). "Sobre la comprensión de tipos, abstracción de datos y polimorfismo" (PDF) . ACM Computing Surveys . 17 (4): 471–523. CiteSeerX  10.1.1.117.695 . doi :10.1145/6041.6042. S2CID  2921816.:"Los tipos polimórficos son tipos cuyas operaciones son aplicables a valores de más de un tipo".
  2. ^ Stroustrup, Bjarne (19 de febrero de 2007). "Bjarne Stroustrup's C++ Glossary". polimorfismo: proporcionar una única interfaz para entidades de distintos tipos.
  3. ^ "Polimorfismo". Tutoriales de Java: aprendizaje del lenguaje Java: interfaces y herencia . Oracle . Consultado el 8 de septiembre de 2021 .
  4. ^ 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.
  5. ^ ab Strachey, Christopher (2000). "Conceptos fundamentales en lenguajes de programación". Computación simbólica y de orden superior . 13 (1/2): 11–49. CiteSeerX 10.1.1.332.3161 . doi :10.1023/A:1010000313106. ISSN  1573-0557. S2CID  14124601. 
  6. ^ Tucker, Allen B. (2004). Manual de Ciencias de la Computación (2.ª ed.). Taylor & Francis. pp. 91–. ISBN 978-1-58488-360-9.
  7. ^ Pierce, BC (2002). "23.2 Variedades de polimorfismo". Tipos y lenguajes de programación . MIT Press. págs. 340-1. ISBN 9780262162098.
  8. ^ Wand, Mitchell (junio de 1989). "Inferencia de tipos para concatenación de registros y herencia múltiple". Actas. Cuarto Simposio Anual sobre Lógica en Ciencias de la Computación . págs. 92–97. doi :10.1109/LICS.1989.39162.
  9. ^ Lämmel, Ralf; Visser, Joost (2002). "Combinadores tipados para recorrido genérico". Aspectos prácticos de los lenguajes declarativos: 4.º simposio internacional . Springer. pp. 137–154, véase p. 153. CiteSeerX 10.1.1.18.5727 . ISBN.  354043092X.
  10. ^ Slepak, Justin; Shivers, Olin; Manolios, Panagiotis (2019). "La semántica del polimorfismo de rango". arXiv : 1907.00509 [cs.PL].
  11. ^ Beingessner, Alexis. "Cómo Swift logró la vinculación dinámica donde Rust no pudo".

Enlaces externos