Articulo de referencia

Unión etiquetada

En informática , una unión etiquetada , también llamada variante , registro variante , tipo de elección , unión discriminada , unión disjunta , tipo suma o coproducto , es una e...

En informática , una unión etiquetada , también llamada variante , registro variante , tipo de elección , unión discriminada , unión disjunta , tipo suma o coproducto , es una estructura de datos que se utiliza para almacenar un valor que puede adoptar varios tipos diferentes, pero fijos. Solo uno de los tipos puede estar en uso a la vez, y un campo de etiqueta indica explícitamente qué tipo se está utilizando. Se puede considerar como un tipo que tiene varios "casos", cada uno de los cuales debe manejarse correctamente cuando se manipula dicho tipo. Esto es fundamental para definir tipos de datos recursivos, en los que algún componente de un valor puede tener el mismo tipo que ese valor; por ejemplo, al definir un tipo para representar árboles , donde es necesario distinguir subárboles de múltiples nodos y hojas. Al igual que las uniones ordinarias , las uniones etiquetadas pueden ahorrar espacio de almacenamiento al superponer áreas de almacenamiento para cada tipo, ya que solo uno está en uso a la vez.

Descripción

Las uniones etiquetadas son de suma importancia en lenguajes de programación funcional como ML y Haskell , donde se denominan tipos de datos (véase tipo de dato algebraico ) y el compilador puede verificar que todos los casos de una unión etiquetada se manejan siempre, evitando así muchos tipos de errores. Los tipos de suma verificada en tiempo de compilación también se utilizan ampliamente en Rust , donde se denominan enumeraciones . Sin embargo, pueden construirse en casi cualquier lenguaje de programación y son mucho más seguras que las uniones sin etiquetar, a menudo llamadas simplemente uniones, que son similares pero no registran explícitamente qué miembro de una unión se está utilizando en cada momento.

Las uniones etiquetadas suelen ir acompañadas del concepto de constructor , que es similar, pero no idéntico, al constructor de una clase . Un constructor es una función o expresión que produce un valor del tipo de unión etiquetada, a partir de una etiqueta y un valor del tipo correspondiente.

Matemáticamente, las uniones etiquetadas corresponden a uniones disjuntas o discriminadas , que generalmente se escriben usando +. Dado un elemento de una unión disjuntaA+B{\displaystyle A+B}, es posible determinar si provino deA{\displaystyle A}oB{\displaystyle B}. Si un elemento se encuentra en ambos, habrá dos copias efectivamente distintas del valor enA+B{\displaystyle A+B}, uno deA{\displaystyle A}y uno deB{\displaystyle B}.

En teoría de tipos , una unión etiquetada se llama tipo suma . Los tipos suma son el dual de los tipos producto . Las notaciones varían, pero generalmente el tipo sumaA+B{\displaystyle A+B}viene con dos formas de introducción ( inyecciones )inyección1:AA+B{\displaystyle {\texttt {inj}}_{1}:A\mapsto A+B}yinyección2:BA+B{\displaystyle {\texttt {inj}}_{2}:B\mapsto A+B}. La forma de eliminación es el análisis de casos, conocido como coincidencia de patrones en lenguajes de estilo ML : simi{\displaystyle e}tiene tipoA+B{\displaystyle A+B}ymi1{\displaystyle e_{1}}ymi2{\displaystyle e_{2}}tienen tipoτ{\displaystyle \tau }bajo los supuestosincógnita:A{\displaystyle x:A}yy:B{\displaystyle y:B}respectivamente, entonces el términodoasmi mi oF incógnitami1ymi2{\displaystyle {\mathsf {caso}}\ e\ {\mathsf {de}}\ x\Rightarrow e_{1}\mid y\Rightarrow e_{2}}tiene tipoτ{\displaystyle \tau }. El tipo suma corresponde a la disyunción lógica intuicionista bajo la correspondencia de Curry-Howard .

Un tipo enumerado puede considerarse un caso degenerado: una unión etiquetada de tipos unitarios . Corresponde a un conjunto de constructores nulos y puede implementarse como una simple variable de etiqueta, ya que no contiene datos adicionales aparte del valor de la etiqueta.

Muchas técnicas de programación y estructuras de datos, incluyendo la técnica de "rope" , la evaluación perezosa , la jerarquía de clases (ver más abajo), la aritmética de precisión arbitraria , la codificación CDR , el bit de indirección y otros tipos de punteros etiquetados , generalmente se implementan utilizando algún tipo de unión etiquetada.

Una unión etiquetada puede considerarse como el tipo más simple de formato de datos autodescriptivo . La etiqueta de la unión etiquetada puede considerarse como el tipo más simple de metadatos .

En lenguajes con tipado sensible al flujo , las uniones etiquetadas pueden implementarse mediante una combinación de tipos de unión y tipos de registro . [ 1 ]

Ventajas y desventajas

La principal ventaja de una unión etiquetada sobre una unión sin etiquetar es que todos los accesos son seguros, e incluso el compilador puede verificar que se gestionen todos los casos. Las uniones sin etiquetar dependen de la lógica del programa para identificar correctamente el campo activo, lo que puede provocar un comportamiento extraño y errores difíciles de detectar si dicha lógica falla.

La principal ventaja de una unión etiquetada sobre un registro simple con un campo para cada tipo es que ahorra espacio de almacenamiento al superponer el almacenamiento para todos los tipos. Algunas implementaciones reservan suficiente espacio de almacenamiento para el tipo más grande, mientras que otras ajustan dinámicamente el tamaño del valor de la unión etiquetada según sea necesario. Cuando el valor es inmutable , es sencillo asignar solo el espacio de almacenamiento necesario.

La principal desventaja de las uniones etiquetadas es que la etiqueta ocupa espacio. Dado que generalmente hay pocas alternativas, la etiqueta a menudo se puede comprimir en 2 o 3 bits donde haya espacio disponible, pero a veces ni siquiera estos bits están disponibles. En este caso, una alternativa útil pueden ser las etiquetas plegadas , calculadas o codificadas , donde el valor de la etiqueta se calcula dinámicamente a partir del contenido del campo de unión. Ejemplos comunes son el uso de valores reservados , donde, por ejemplo, una función que devuelve un número positivo puede devolver -1 para indicar un fallo, y valores centinela , que se utilizan con mayor frecuencia en punteros etiquetados .

En ocasiones, las uniones sin etiquetar se utilizan para realizar conversiones a nivel de bits entre tipos, o para la manipulación de tipos . Las uniones etiquetadas no están diseñadas para este propósito; normalmente se asigna un nuevo valor cada vez que se cambia la etiqueta.

Muchos lenguajes admiten, en cierta medida, un tipo de dato universal , que incluye todos los valores de cualquier otro tipo, y a menudo se proporciona una forma de comprobar el tipo real de un valor del tipo universal. A estos se les suele llamar variantes . Si bien los tipos de datos universales son comparables a las uniones etiquetadas en su definición formal, las uniones etiquetadas típicas incluyen un número relativamente pequeño de casos, y estos casos forman diferentes maneras de expresar un único concepto coherente, como un nodo de estructura de datos o una instrucción. Además, se espera que se traten todos los casos posibles de una unión etiquetada cuando se utilice. Los valores de un tipo de dato universal no están relacionados y no hay una forma factible de tratarlos todos.

Al igual que los tipos de opciones y el manejo de excepciones , las uniones etiquetadas se utilizan a veces para manejar la ocurrencia de resultados excepcionales. A menudo, estas etiquetas se incorporan al tipo como valores reservados , y su ocurrencia no se verifica de forma consistente: esta es una fuente bastante común de errores de programación. Este uso de uniones etiquetadas se puede formalizar como una mónada con las siguientes funciones:

devolver:A(A+mi)=avalora{\displaystyle {\texttt {return}}\colon A\to \left(A+E\right)=a\mapsto {\text{value}}\,a}
unir:(A+mi)(A(B+mi))(B+mi)=aF{errarmisi a=errarmiFasi a=valora{\displaystyle {\texttt {bind}}\colon \left(A+E\right)\to \left(A\to \left(B+E\right)\right)\to \left(B+E\right)=a\mapsto f\mapsto {\begin{cases}{\text{err}}\,e&{\text{si}}\ a={\text{err}}\,e\\f\,a'&{\text{si}}\ a={\text{valor}}\,a'\end{cases}}}

dóndevalor{\displaystyle {\text{valor}}}yerrar{\displaystyle {\text{error}}}son los constructores del tipo unión,A{\displaystyle A}yB{\displaystyle B}son tipos de resultados válidos ymi{\displaystyle E}es el tipo de condiciones de error. Alternativamente, la misma mónada puede describirse mediantedevolver{\displaystyle {\texttt {return}}}y dos funciones adicionales,fmap{\displaystyle {\texttt {fmap}}}yunirse{\displaystyle {\texttt {join}}}:

fmap:(AB)((A+mi)(B+mi))=Fa{errarmisi a=errarmivalor(Fa)si a=valora{\displaystyle {\texttt {fmap}}\colon (A\to B)\to \left(\left(A+E\right)\to \left(B+E\right)\right)=f\mapsto a\mapsto {\begin{cases}{\text{err}}\,e&{\text{if}}\ a={\text{err}}\,e\\{\text{value}}\,{\text{(}}\,f\,a'\,{\text{)}}&{\text{if}}\ a={\text{value}}\,a'\end{cases}}}
unirse:((A+mi)+mi)(A+mi)=a{errarmisi a=errarmierrarmisi a=valor(errarmi)valorasi a=valor(valora){\displaystyle {\texttt {join}}\colon ((A+E)+E)\to (A+E)=a\mapsto {\begin{cases}{\text{err}}\,e&{\mbox{if}}\ a={\text{err}}\,e\\{\text{err}}\,e&{\text{if}}\ a={\text{value}}\,{\text{(err}}\,e\,{\text{)}}\\{\text{value}}\,a'&{\text{if}}\ a={\text{value}}\,{\text{(value}}\,a'\,{\text{)}}\end{cases}}}

Ejemplos

Digamos que queremos construir un árbol binario de enteros. En ML, lo haríamos creando un tipo de dato como este:

tipo de dato árbol = Hoja | Nodo de ( int * árbol * árbol )

Esta es una unión etiquetada con dos casos: uno, la hoja, se usa para terminar una ruta del árbol y funciona de manera muy similar a como lo haría un valor nulo en lenguajes imperativos. La otra rama contiene un nodo, que a su vez contiene un número entero y un subárbol izquierdo y derecho. Hoja y Nodo son los constructores que nos permiten generar un árbol específico, como por ejemplo:

Nodo ( 5 , Nodo ( 1 , Hoja , Hoja ), Nodo ( 3 , Hoja , Nodo ( 4 , Hoja , Hoja )))

que corresponde a este árbol:

El árbol producido por los constructores anteriores

Ahora podemos escribir fácilmente una función con tipado seguro que, por ejemplo, cuente el número de nodos en el árbol:

fun countNodes ( Leaf ) = 0 | countNodes ( Node ( int , left , right )) = 1 + countNodes ( left ) + countNodes ( right )

Soporte de idiomas

ALGOL 68

En ALGOL 68 , las uniones etiquetadas se denominan modos unidos , la etiqueta es implícita y la caseconstrucción se utiliza para determinar qué campo está etiquetado:

modenode = union (real, int, compl, string);

Ejemplo de uso unioncasepara node:

nodo n := "1234"; caso n en ( real r): imprimir(("real:", r)),  ( int i): print(("int:", i)), ( compl c): print(("compl:", c)), ( cadena s): print(("cadena:", s)) out print(("?:", n)) esac

En ALGOL 68, una unión puede ser automáticamente convertida en una unión más amplia, por ejemplo, si todos sus constituyentes pueden ser manejados por el parámetro de unión de print , una unión puede simplemente pasarse a print como en el caso de out anterior.

Ada

En Ada , estos se denominan "tipos discriminados".

tipo Shape_Kind es ( Cuadrado , Rectángulo , Círculo ); tipo Shape ( Tipo : Shape_Kind ) es registro Centro_X : Entero ; Centro_Y : Entero ; caso Tipo es cuando Cuadrado => Lado : Entero ; cuando Rectángulo => Ancho , Alto : Entero ; cuando Círculo => Radio : Entero ; fin caso ; fin registro ;-- Cualquier intento de acceder a un miembro cuya existencia depende -- de un cierto valor del discriminante, cuando el -- discriminante no es el esperado, genera un error.

C++

Mientras que C solo proporciona unionun tipo de unión sin etiquetar, C++ ofrece una clase (desde C++17 ), pero en lugar de ser una característica central del lenguaje como , es una clase de la biblioteca estándar .std::variant<Ts...>union

importar std ;using std :: string ; using std :: variant ;struct Cat { string nombre ; };struct Perro { string nombre ; };struct Bird { string nombre ; };usando Mascota = variante < Gato , Perro , Pájaro > ;Mascota p1 = Gato ( "Bigotes" ); Mascota p2 = Perro ( "Rex" );

El "patrón de sobrecarga" es un patrón de diseño común que implementa la coincidencia de patrones algebraicos mediante herencia de plantillas variádicas . [ 2 ]

// Tipo auxiliar para la plantilla visitante < typename ... Ts > struct Overload : public Ts ... { using Ts :: operator ()...; }// Plantilla de guía de deducción < typename ... Ts > Sobrecarga ( Ts ...) -> Sobrecarga < Ts ... > ;

Luego, la std::visit()función puede usarse para llamar a cada caso respectivo, y cada caso es manejado por una lambda : [ 3 ]

usando Valor = variante < int , double , string > ;Valor v = 3,14 ;// imprime "double: 3.14" std :: visit ( Overload { []( int i ) -> void { std :: println ( "int: {}" , i ); }, []( double d ) -> void { std :: println ( "double: {}" , d ); }, []( const string & s ) -> void { std :: println ( "string: {}" , s ); } }, v );

Además, std::visit()puede hacerse que devuelva un tipo:

Mascota p = Pájaro ( "Polly" );string nombre = std :: visit ( Sobrecarga { [ ] ( const Gato & c ) -> cadena { return c.nombre ; } , []( const Perro & d ) - > cadena { return d.nombre ; } , [ ]( const Pájaro & b ) -> cadena { return b.nombre ; } }, p ) ;// Imprime "Nombre de la mascota: Polly" std :: println ( "Nombre de la mascota: {}" , name );

El tipo de resultado para el manejo de errores (como en Rust), se introdujo en C++23 como .std::result::Result<T,E>std::expected<T,E>

DO#

Tradicionalmente, C# no tenía uniones etiquetadas. La forma más cercana de aproximarlas era mediante la coincidencia de patrones sobre recordtipos. Con C# 15, se introdujeron las uniones etiquetadas con la palabra clave union. [ 4 ]

clase de registro Coche ( cadena Modelo ); clase de registro Bicicleta ( cadena Modelo ); clase de registro Autobús ( cadena Modelo );Vehículo de la unión ( automóvil , bicicleta , autobús );Vehículo coche = nuevo Coche ( "Tesla Model 3" ); Consola . WriteLine ( coche . Value ); // Coche { Modelo = Tesla Model 3 }Vehículo bicicleta = nueva Bicicleta ( "Giant Escape 3" ); Consola . WriteLine ( bicicleta . Value ); // Bicicleta { Modelo = Giant Escape 3 }Vehículo autobús = nuevo Autobús ( "Volvo 9700" ); Consola . WriteLine ( autobús . Value ); // Autobús { Modelo = Volvo 9700 }Vehículo v = /* algún vehículo aquí */ ; string modelo = v switch { Coche c => c . Modelo , Bicicleta bk => b . Modelo , Autobús bs => bs . Modelo , };

El defaultvalor de una unión es null, pero si todos los tipos en la unión no son anulables , entonces una switchexpresión no necesita comprobar null.

Ciclón

Cyclone , un dialecto de C con mejoras de seguridad, ofrece uniones etiquetadas. [ 5 ] Estas se declararon con un @taggedcalificador.

@tagged unión Foo { int i ; double d ; char * @fat s ; };void printFoo ( union Foo x ) { // El compilador advertiría de un caso omitido switch ( x ) { case { .i = i }: printf ( "%d" , i ); break ; case { .d = d } : printf ( "%g" , d ); break ; case { .s = s }: printf ( " % s" , s ) ; break ; } }

D

D proporciona el std.variantmódulo, con tipos como Variant(que representa cualquier tipo ) y (que representa un tipo de datos algebraico). [ 6 ]std.variant.Algebraic!(T...)

import std.variant ;Algebraico !( int , string ) v = 10 ;int resultado = v.visit !( ( string s ) => cast ( int ) s.length , ( int i ) = > i , () = > -1 ) ( ); writeln ( resultado ) ; // 10

F#

F# ha discriminado a los sindicatos:

tipo Árbol = | Hoja | Nodo de valor : int * izquierda : Árbol * derecha : Árbolsea ​​árbol = Nodo ( 5 , Nodo ( 1 , Hoja , Hoja ), Nodo ( 3 , Hoja , Nodo ( 4 , Hoja , Hoja )))

Dado que los casos definidos son exhaustivos, el compilador puede comprobar que todos los casos se manejan en una coincidencia de patrones:

match tree with | Node ( x , _, _) -> printfn "valor del nodo de nivel superior: %i" x | Leaf -> printfn "el nodo de nivel superior es una hoja"

Haxe

Las enumeraciones de Haxe también funcionan como uniones etiquetadas: [ 7 ]

enum Color { Rojo ; Verde ; Azul ; RGB ( r : Int , g : Int , b : Int ); }

Estos elementos se pueden emparejar mediante una expresión switch:

switch ( color ) { case Red : trace ( "El color era rojo" ); case Green : trace ( "El color era verde" ); case Blue : trace ( "El color era azul" ); case Rgb ( r , g , b ): trace ( "El color tenía un valor rojo de " + r ); }

Java

En Java , la forma más cercana de implementar una unión etiquetada es mediante una clase sellada , que restringe directamente qué tipos pueden heredar la clase. La coincidencia de patrones en sí misma puede realizarse sobre switchexpresiones. [ 8 ]

La interfaz sellada Shape permite Circle , Rectangle , Triangle {} registra Circle ( double radius ) implementa Shape {} registra Rectangle ( double width , double height ) implementa Shape {} registra Triangle ( double base , double height ) implementa Shape {}área doble ( Forma s ) { return switch ( forma ) { case Círculo ( doble r ) -> Math . PI * r * r ; case Rectángulo ( doble w , doble h ) -> w * h ; case Triángulo ( doble b , doble h ) -> 0.5 * b * h ; }; }

ML estándar

En Standard ML , una unión etiquetada es un "tipo de datos algebraico" o un "tipo de suma".

tipo de datos forma = Círculo de real | Rectángulo de real * real | Puntoárea de diversión s = caso s de Círculo r => 3.1415926535 * r * r | Rectángulo ( w , h ) => w * h | Punto => 0.0

Nim

Nim tiene variantes de objetos [ 9 ] similares en declaración a las de Pascal y Ada:

tipo ShapeKind = enumeración skSquare , skRectangle , skCircle Forma = objeto centroX , centroY : entero caso tipo : ShapeKind de skSquare : lado : entero de skRectangle : longitud , altura : entero de skCircle : radio : entero

Las macros se pueden usar para emular la coincidencia de patrones o para crear azúcar sintáctico para declarar variantes de objetos, como se ve aquí implementado por el paquete patty :

hamburguesa importadaproc `~` [ A ] ( a : A ): ref A = new ( result ) result [] = aLista de variantes [ A ] : Nil Cons ( x : A , xs : ref Lista [ A ] )proc listHelper [ A ] ( xs : seq [ A ] ): List [ A ] = if xs . len == 0 : Nil [ A ] () else : Cons ( xs [ 0 ] , ~ listHelper ( xs [ 1 .. xs . high ] ))proc list [ A ] ( xs : varargs [ A ] ): List [ A ] = listHelper ( @ xs )proc sum ( xs : List [ int ] ): int = ( block : match xs : Nil : 0 Cons ( y , ys ): y + sum ( ys [] ) )echo suma ( lista ( 1 , 2 , 3 , 4 , 5 ))

OCaml

En OCaml , la sintaxis de unión etiquetada varía ligeramente con respecto a Standard ML, pero sigue siendo prácticamente la misma.

tipo forma = | Círculo de float | Rectángulo de float * float | Puntolet area = function | Circle r -> Float . pi *. r *. r | Rectangle ( w , h ) -> w *. h | Point -> 0 . 0

Pascal

En Pascal , estos se denominan "registros variantes".

tipo shapeKind = ( cuadrado , rectángulo , círculo ) ; forma = registro centrox : entero ; centroy : entero ; caso tipo : shapeKind de cuadrado : ( lado : entero ) ; rectángulo : ( ancho , alto : entero ) ; círculo : ( radio : entero ) ; fin ;

Pitón

Python 3.9 introduce soporte para anotaciones de tipado que se pueden usar para definir un tipo de unión etiquetado (PEP-593 [ 10 ] ):

from typing import Annotated , TypedDictMoneda = Anotado [ TypedDict ( 'Moneda' , { 'dólares' : float , 'libras' : float }, total = False ), TaggedUnion , ]

Óxido

El lenguaje Rust tiene un amplio soporte para uniones etiquetadas, llamadas enumeraciones. [ 11 ] Por ejemplo:

enum Árbol { Hoja , Nodo ( i64 , Caja < Árbol > , Caja < Árbol > ) }

También permite la coincidencia en uniones:

let árbol : Árbol = Árbol :: Nodo ( 2 , Caja :: nuevo ( Árbol :: Nodo ( 0 , Caja :: nuevo ( Árbol :: Hoja ), Caja :: nuevo ( Árbol :: Hoja ))), Caja :: nuevo ( Árbol :: Nodo ( 3 , Caja :: nuevo ( Árbol :: Hoja ), Caja :: nuevo ( Árbol :: Nodo ( 4 , Caja :: nuevo ( Árbol :: Hoja ), Caja :: nuevo ( Árbol :: Hoja ))))) );fn add_values ​​( árbol : Árbol ) -> i64 { match árbol { Árbol :: Nodo ( v , a , b ) => v + add_values ​​( * a ) + add_values ​​( * b ), Árbol :: Hoja => 0 } }fn main () { assert_eq! ( add_values ​​( tree ), 9 ); }

El modelo de manejo de errores de Rust se basa extensamente en estas uniones etiquetadas, especialmente en el tipo, que es o , y en el tipo, que es o . [ 12 ]std::option::Option<T>NoneSome(T)std::result::Result<T,E>Ok(T)Err(E)

Scala

Scala tiene clases de caso:

clase abstracta sellada Tree objeto de caso Leaf extiende Tree clase de caso Node ( valor : Int , izquierda : Tree , derecha : Tree ) extiende Treeval árbol = Nodo ( 5 , Nodo ( 1 , Hoja , Hoja ), Nodo ( 3 , Hoja , Nodo ( 4 , Hoja , Hoja )))

Debido a que la jerarquía de clases está sellada, el compilador puede comprobar que todos los casos se manejan mediante una coincidencia de patrones:

coincidencia de árbol { caso Nodo ( x , _ , _ ) => println ( "valor del nodo de nivel superior: " + x ) caso Hoja => println ( "el nodo de nivel superior es una hoja" ) }

Las clases de caso de Scala también permiten la reutilización mediante subtipado:

clase abstracta sellada Shape ( centroX : Int , centroY : Int ) caso clase Cuadrado ( lado : Int , centroX : Int , centroY : Int ) extiende Shape ( centroX , centroY ) caso clase Rectángulo ( longitud : Int , altura : Int , centroX : Int , centroY : Int ) extiende Shape ( centroX , centroY ) caso clase Círculo ( radio : Int , centroX : Int , centroY : Int ) extiende Shape ( centroX , centroY )

Los enumerados se agregaron en Scala 3, [ 13 ] lo que nos permite reescribir los ejemplos anteriores de Scala de manera más concisa:

enum Tree [ + T ]: case Leaf case Node ( x : Int , left : Tree [ T ], right : Tree [ T ])enum Shape ( centerX : Int , centerY : Int ): case Square ( side : Int , centerX : Int , centerY : Int ) extends Shape ( centerY , centerX ) case Rectangle ( length : Int , height : Int , centerX : Int , centerY : Int ) extends Shape ( centerX , centerY ) case Circle ( radius : Int , centerX : Int , centerY : Int ) extends Shape ( centerX , centerY )

Rápido

Swift también ofrece un soporte sustancial para uniones etiquetadas mediante enumeraciones. [ 14 ] Por ejemplo:

enum Tree { case leaf indirect case node ( Int , Tree , Tree ) }let árbol = Árbol . nodo ( 2 , . nodo ( 0 , . hoja , . hoja ), . nodo ( 3 , . hoja , . nodo ( 4 , . hoja , . hoja )) )func add_values ​​( _ tree : Tree ) -> Int { switch tree { case let . node ( v , a , b ): return v + add_values ​​( a ) + add_values ​​( b )caso . hoja : devolver 0 } }assert ( add_values ​​( árbol ) == 9 )

Mecanografiado

Con TypeScript también es posible crear uniones etiquetadas. Por ejemplo:

interfaz Hoja { tipo : "hoja" ; }interfaz Nodo { tipo : "nodo" ; valor : número ; izquierda : Árbol ; derecha : Árbol ; }tipo Árbol = Hoja | Nodo ;const raíz : Árbol = { tipo : "nodo" , valor : 5 , izquierda : { tipo : "nodo" , valor : 1 , izquierda : { tipo : "hoja" }, derecha : { tipo : "hoja" } }, derecha : { tipo : "nodo" , valor : 3 , izquierda : { tipo : "hoja" }, derecha : { tipo : "nodo" , valor : 4 , izquierda : { tipo : "hoja" }, derecha : { tipo : "hoja" } } } };function visit ( tree : Tree ) { switch ( tree.kind ) { case " leaf " : break ; case " node " : console.log ( tree.value ) ; visit ( tree.left ) ; visit ( tree.right ) ; break ; } }

Jerarquías de clase como uniones etiquetadas

En una jerarquía de clases típica de programación orientada a objetos , cada subclase puede encapsular datos exclusivos de esa clase. Los metadatos utilizados para realizar búsquedas de métodos virtuales (por ejemplo, el puntero a la tabla virtual del objeto en la mayoría de las implementaciones de C++) identifican la subclase y, por lo tanto, actúan como una etiqueta que identifica los datos almacenados por la instancia (véase RTTI ). El constructor de un objeto establece esta etiqueta, que permanece constante durante toda la vida útil del objeto.

Sin embargo, una jerarquía de clases implica un verdadero polimorfismo de subtipos . Se puede extender creando subclases adicionales del mismo tipo base, lo cual no se podría gestionar correctamente con un modelo de etiquetas/despacho. Por lo tanto, normalmente no es posible realizar análisis de casos ni despachos en la "etiqueta" de un subobjeto, como se haría con las uniones etiquetadas. Algunos lenguajes, como Scala, permiten que las clases base sean "selladas" y unifican las uniones etiquetadas con clases base selladas.

Véase también

Referencias

  1. https://arxiv.org/pdf/2111.03354 pág. 8
  2. Bartlomiej Filipek (10 de noviembre de 2023). "2 líneas de código y 3 características de C++17: el patrón de sobrecarga" . isocpp.org . ISO C++.
  3. cppreference.com (29 de junio de 2026). "std::visit" . cppreference.com . cppreference.com.
  4. Bill Wagner (2 de abril de 2026). "Explora los tipos de unión en C# 15" . devblogs.microsoft.com . Microsoft.
  5. "Ciclón: Sindicatos etiquetados" .
  6. Andrei Alexandrescu (29 de junio de 2026). "variante.estándar" . dlang.org . Lenguaje D.
  7. "Uso de enumeraciones - Haxe - El kit de herramientas multiplataforma" . Fundación Haxe.
  8. Oracle Corporation (29 de junio de 2026). "Expresiones Switch" . docs.oracle.com . Oracle Corporation.
  9. "Manual de Nim" . nim-lang.org . Consultado el 23 de enero de 2020 .
  10. "PEP 593 -- Anotaciones flexibles de funciones y variables" . Python.org . Consultado el 20 de junio de 2021 .
  11. "El lenguaje de programación Rust" . Mozilla.
  12. "Rust By Example" . Mozilla.
  13. "Referencia del lenguaje Scala 3: Enumeraciones" . El equipo de Scala.
  14. "Enumeraciones — El lenguaje de programación Swift (Swift 5.4)" . docs.swift.org . Consultado el 28 de abril de 2021 .