Aprendimos a crear tipos personalizados con la palabra clave type. Nuestro ejemplo principal fue un usuario en una sala de chat:
type User
= Regular String Int
| Visitor String
Los usuarios habituales tienen nombre y edad, mientras que los visitantes solo tienen nombre. Ya tenemos nuestro tipo personalizado, pero ¿cómo lo usamos?
Supongamos que queremos una función toName que decida el nombre que se mostrará para cada usuario. Necesitamos usar una expresión case:
toName : User -> String
toName user =
case user of
Regular name age ->
name
Visitor name ->
name
-- toName (Regular "Thomas" 44) == "Thomas"
-- toName (Visitor "kate95") == "kate95"
La expresión case nos permite ramificar según la variante que veamos, así que, independientemente de si vemos a Thomas o a Kate, siempre sabremos cómo mostrar su nombre.
Y si probamos argumentos inválidos como toName (Visitar "kate95") o toName Anonymous, el compilador nos lo notifica inmediatamente. Esto significa que muchos errores simples se pueden corregir en segundos, en lugar de informar a los usuarios y consumir mucho más tiempo.
La función toName que acabamos de definir funciona de maravilla, pero ¿se da cuenta de que la edad no se utiliza en la implementación? Cuando algunos de los datos asociados no se utilizan, es habitual usar un comodín en lugar de asignarles un nombre:
toName : User -> String
toName user =
case user of
Regular name _ ->
name
Visitor name ->
name
El _ reconoce los datos, pero también indica explícitamente que nadie los está utilizando.