Las uniones discriminadas son una característica poderosa y flexible de F# que permite definir tipos que pueden representar uno de varios casos posibles. Son similares a las enumeraciones en otros lenguajes, pero mucho más expresivas y útiles para modelar datos complejos.
Conceptos Clave
- Definición de Uniones Discriminadas: Cómo declarar un tipo de unión discriminada.
- Casos de Unión: Diferentes formas en que se pueden definir los casos de una unión discriminada.
- Coincidencia de Patrones: Uso de la coincidencia de patrones para trabajar con uniones discriminadas.
- Ejemplos Prácticos: Ejemplos de uso en situaciones del mundo real.
Definición de Uniones Discriminadas
En F#, una unión discriminada se define utilizando la palabra clave type
seguida del nombre del tipo y los diferentes casos que puede tomar.
En este ejemplo, Shape
es una unión discriminada que puede ser un Circle
con un radio (float
), un Rectangle
con ancho y alto (float * float
), o un Triangle
con tres lados (float * float * float
).
Casos de Unión
Cada caso de una unión discriminada puede tener diferentes tipos de datos asociados. Aquí hay algunos ejemplos adicionales:
En estos ejemplos, Result<'T>
puede ser un Success
con un valor de tipo 'T
o un Error
con un mensaje de error (string
). Option<'T>
puede ser Some
con un valor de tipo 'T
o None
.
Coincidencia de Patrones
La coincidencia de patrones es una técnica poderosa para trabajar con uniones discriminadas. Permite descomponer y procesar los diferentes casos de una unión discriminada de manera clara y concisa.
let describeShape shape = match shape with | Circle radius -> sprintf "Circle with radius %f" radius | Rectangle (width, height) -> sprintf "Rectangle with width %f and height %f" width height | Triangle (a, b, c) -> sprintf "Triangle with sides %f, %f, %f" a b c
En este ejemplo, la función describeShape
toma un Shape
y utiliza la coincidencia de patrones para devolver una descripción del mismo.
Ejemplos Prácticos
Ejemplo 1: Modelado de Resultados de Operaciones
type OperationResult = | Success of string | Failure of string let performOperation x y = if y = 0 then Failure "Division by zero" else Success (sprintf "Result is %f" (float x / float y)) let result = performOperation 10 0 match result with | Success message -> printfn "Operation succeeded: %s" message | Failure error -> printfn "Operation failed: %s" error
Ejemplo 2: Manejo de Opciones
type Option<'T> = | Some of 'T | None let findElement list element = if List.contains element list then Some element else None let result = findElement [1; 2; 3] 2 match result with | Some value -> printfn "Element found: %d" value | None -> printfn "Element not found"
Ejercicio Práctico
Ejercicio 1: Modelar un Sistema de Tickets
Define una unión discriminada Ticket
que pueda representar tres tipos de tickets: Bug
, FeatureRequest
y Support
. Cada tipo de ticket debe tener diferentes datos asociados.
type Ticket = | Bug of string * int | FeatureRequest of string * string | Support of string let describeTicket ticket = match ticket with | Bug (description, severity) -> sprintf "Bug: %s (Severity: %d)" description severity | FeatureRequest (description, requester) -> sprintf "Feature Request: %s (Requested by: %s)" description requester | Support description -> sprintf "Support: %s" description // Prueba tu función con diferentes tipos de tickets let ticket1 = Bug ("Null pointer exception", 1) let ticket2 = FeatureRequest ("Add dark mode", "Alice") let ticket3 = Support "Cannot login" printfn "%s" (describeTicket ticket1) printfn "%s" (describeTicket ticket2) printfn "%s" (describeTicket ticket3)
Solución
type Ticket = | Bug of string * int | FeatureRequest of string * string | Support of string let describeTicket ticket = match ticket with | Bug (description, severity) -> sprintf "Bug: %s (Severity: %d)" description severity | FeatureRequest (description, requester) -> sprintf "Feature Request: %s (Requested by: %s)" description requester | Support description -> sprintf "Support: %s" description // Prueba tu función con diferentes tipos de tickets let ticket1 = Bug ("Null pointer exception", 1) let ticket2 = FeatureRequest ("Add dark mode", "Alice") let ticket3 = Support "Cannot login" printfn "%s" (describeTicket ticket1) printfn "%s" (describeTicket ticket2) printfn "%s" (describeTicket ticket3)
Conclusión
Las uniones discriminadas son una herramienta poderosa en F# para modelar datos que pueden tener múltiples formas. Combinadas con la coincidencia de patrones, permiten escribir código claro y conciso para manejar diferentes casos de datos. En el próximo módulo, exploraremos cómo las uniones discriminadas se integran con otras características de F# para crear programas robustos y expresivos.
Curso de Programación en F#
Módulo 1: Introducción a F#
Módulo 2: Conceptos Básicos
- Tipos de Datos y Variables
- Funciones e Inmutabilidad
- Coincidencia de Patrones
- Colecciones: Listas, Arreglos y Secuencias
Módulo 3: Programación Funcional
- Funciones de Orden Superior
- Recursión
- Encadenamiento y Composición
- Aplicación Parcial y Currificación
Módulo 4: Estructuras de Datos Avanzadas
Módulo 5: Programación Orientada a Objetos en F#
- Clases y Objetos
- Herencia e Interfaces
- Mezclando Programación Funcional y Orientada a Objetos
- Módulos y Espacios de Nombres
Módulo 6: Programación Asíncrona y Paralela
- Flujos de Trabajo Asíncronos
- Biblioteca de Tareas Paralelas
- MailboxProcessor y Agentes
- Patrones de Concurrencia
Módulo 7: Acceso y Manipulación de Datos
Módulo 8: Pruebas y Depuración
- Pruebas Unitarias con NUnit
- Pruebas Basadas en Propiedades con FsCheck
- Técnicas de Depuración
- Perfilado de Rendimiento