Articulo de referencia

Sistema de tipos estructurales

Un sistema de tipos estructural (o sistema de tipos basado en propiedades ) es una clase importante de sistemas de tipos en la que la compatibilidad y equivalencia de tipos se d...

Un sistema de tipos estructural (o sistema de tipos basado en propiedades ) es una clase importante de sistemas de tipos en la que la compatibilidad y equivalencia de tipos se determinan por la estructura o definición real del tipo y no por otras características como su nombre o lugar de declaración. Los sistemas estructurales se utilizan para determinar si los tipos son equivalentes y si un tipo es un subtipo de otro. Se diferencia de los sistemas nominativos , donde las comparaciones se basan en los nombres de los tipos o declaraciones explícitas, y del tipado dinámico (duck typing) , en el que solo se comprueba la compatibilidad de la parte de la estructura a la que se accede en tiempo de ejecución.

Descripción

En la tipificación estructural , un elemento se considera compatible con otro si, para cada característica del tipo del segundo elemento, existe una característica correspondiente e idéntica en el tipo del primero. Algunos lenguajes pueden diferir en los detalles, como si las características deben coincidir en nombre. Esta definición no es simétrica e incluye la compatibilidad de subtipos. Dos tipos se consideran idénticos si cada uno es compatible con el otro.

Por ejemplo, OCaml utiliza tipado estructural en los métodos para garantizar la compatibilidad de los tipos de objetos. Go utiliza tipado estructural en los métodos para determinar la compatibilidad de un tipo con una interfaz. Las funciones plantilla de C++ presentan tipado estructural en los argumentos de tipo. Haxe utiliza tipado estructural, pero las clases no tienen subtipos estructurales.

En los lenguajes que admiten polimorfismo de subtipos , se puede establecer una dicotomía similar según cómo se defina la relación de subtipo. Un tipo es un subtipo de otro si y solo si contiene todas las características del tipo base, o subtipos del mismo. El subtipo puede contener características adicionales, como miembros no presentes en el tipo base, o invariantes más fuertes.

Existe una distinción entre la sustitución estructural para el polimorfismo inferido y el no inferido. Algunos lenguajes, como Haskell , no realizan sustitución estructural cuando se declara un tipo esperado (es decir, no inferido), por ejemplo, solo sustituyen funciones que son polimórficas basadas en la firma mediante inferencia de tipos. [ 1 ] Entonces no es posible subtipificar accidentalmente un tipo no inferido, aunque aún puede ser posible proporcionar una conversión explícita a un tipo no inferido, que se invoca implícitamente.

El subtipado estructural es posiblemente más flexible que el subtipado nominativo , ya que permite la creación de tipos y protocolos ad hoc ; en particular, permite la creación de un tipo que es supertipo de un tipo existente, sin modificar la definición de este último. Sin embargo, esto puede no ser deseable cuando el programador desea crear abstracciones cerradas.

Un inconveniente de la tipificación estructural frente a la tipificación nominativa es que dos tipos definidos por separado y destinados a diferentes propósitos, pero que accidentalmente poseen las mismas propiedades (por ejemplo, ambos compuestos por un par de enteros), podrían ser considerados el mismo tipo por el sistema de tipos, simplemente porque tienen una estructura idéntica. Una forma de evitar esto es crear un tipo de dato algebraico para cada uso.

En 1990, Cook et al. demostraron que la herencia no es subtipificación en lenguajes OO con tipado estructural. [ 2 ]

Comprobar que dos tipos son compatibles, basándose en la tipificación estructural, es una operación no trivial, por ejemplo, requiere mantener una pila de tipos comprobados previamente. [ 3 ]

Cuando un tipo no coincide con la estructura esperada, los mensajes de error son más largos que con la tipificación nominal .

Ejemplo

En OCaml, los objetos se tipifican estructuralmente mediante los nombres y tipos de sus métodos.

Los objetos se pueden crear directamente ( objetos inmediatos ) sin pasar por una clase nominal. Las clases solo sirven como funciones para crear objetos.

# let x = object val mutable x = 5 method get_x = x method set_x y = x <- y end ;; val x : < get_x : int ; set_x : int -> unit > = < obj >

Aquí, el entorno de ejecución interactivo de OCaml imprime el tipo inferido del objeto para mayor comodidad. Su tipo ( ) se define únicamente por sus métodos. En otras palabras, el tipo de x se define por los tipos de método "get_x : int" y "set_x : int -> unit" en lugar de por cualquier nombre. [ 4 ]< get_x : int; set_x : int -> unit >  

Para definir otro objeto que tenga los mismos métodos y tipos de métodos:

# let y = object method get_x = 2 method set_x y = Printf . printf "%d \n " y end ;; val y : < get_x : int ; set_x : int -> unit > = < obj >

OCaml los considera del mismo tipo. Por ejemplo, el operador de igualdad está tipado para aceptar solo dos valores del mismo tipo:

# x = y ;; - : bool = false

Por lo tanto, deben ser del mismo tipo, de lo contrario, ni siquiera se realizaría la comprobación de tipos. Esto demuestra que la equivalencia de tipos es estructural.

Se puede definir una función que invoque un método:

# let set_to_10 a = a # set_x 10 ;; val set_to_10 : < set_x : int -> ' a ; .. > -> ' a = < fun >

El tipo inferido para el primer argumento ( ) es interesante. Esto significa que el primer argumento puede ser cualquier objeto que tenga un método "set_x", que toma un int como argumento.< set_x : int -> 'a; .. >..

Por lo tanto, se puede utilizar en el objeto x:

# set_to_10 x ;; - : unidad = ()

Se puede crear otro objeto que tenga ese método y ese tipo de método; los demás métodos son irrelevantes:

# let z = object method blahblah = 2 . 5 method set_x y = Printf . printf "%d \n " y end ;; val z : < blahblah : float ; set_x : int -> unit > = < obj >

La función "set_to_10" también funciona con él:

# set_to_10 z ;; 10 - : unidad = ()

Esto demuestra que la compatibilidad para aspectos como la invocación de métodos está determinada por la estructura.

Definamos un sinónimo de tipo para objetos que solo tengan un método "get_x" y ningún otro método:

# tipo simpler_obj = < get_x : int >;; tipo simpler_obj = < get_x : int >

El objeto xno es de este tipo; pero estructuralmente, xes un subtipo de este tipo, ya que xcontiene un superconjunto de sus métodos. Por lo tanto, xse puede convertir a este tipo:

# ( x :> simpler_obj );; - : simpler_obj = < obj > # ( x :> simpler_obj )# get_x ;; - : int = 10

Pero no objeto z, porque no es un subtipo estructural:

# (z :> objeto_más_simple);; Esta expresión no se puede convertir al tipo simpler_obj = < get_x : int >; tiene tipo < blahblah : float; set_x : int -> unit > pero aquí se usa con tipo < get_x : int; .. > El primer tipo de objeto no tiene el método get_x.

Esto demuestra que la compatibilidad para ampliar las coacciones es estructural.

Referencias

  1. "Polimorfismo basado en firmas" .
  2. Cook, WR; Hill, WL; Canning, PS (enero de 1990). «La herencia no es subtipado». Actas del 17.º simposio ACM SIGPLAN-SIGACT sobre principios de lenguajes de programación - POPL '90 . San Francisco, California. págs. 125-135 . doi : 10.1145/96709.96721 . ISBN  978-0897913430. S2CID 8225906 . {{cite book}}: CS1 mantenimiento: falta el editor de ubicación ( enlace )
  3. "Compatibilidad de tipos: equivalencia de nombre frente a equivalencia estructural" .
  4. "Tipos de objetos" .
Obtenido de " https://en.wikipedia.org/w/index.php?title=Structural_type_system&oldid=1308893561 "