Or-patrones en Haskell
ocaml (3)
En OCaml, estaba acostumbrado a escribir código que parecía:
let combine o1 o2 = match o1, o2 with
| Valid, Invalid | Invalid, Valid -> Invalid
| _ -> ...
No encontré una forma de escribir o-patrones en Haskell y realmente lo extraño. ¿Alguien tiene una solución?
Hay una proposal para agregar o-patrones a GHC.
Hasta entonces (además de los otros ejemplos), pueden emplearse sinónimos de patrón para medios similares:
data ABC = A Int | B Int | C Bool
ab :: ABC -> Maybe Int
ab (A i) = Just i
ab (B i) = Just i
ab C{} = Nothing
pattern AB :: Int -> ABC
pattern AB i <- (ab -> Just i)
Si desea hacer coincidir valores de tipos separados (como Int
y Bool
), puede crear algún tipo existencial restringido
data Showable where
Showable :: Show a => a -> Showable
ac :: ABC -> Maybe Showable
ac (A i) = Just (Showable i)
ac (C b) = Just (Showable b)
ac B{} = Nothing
pattern AC :: () => Show a => a -> ABC
pattern AC showable <- (ac -> Just (Showable showable))
showAC :: ABC -> String
showAC (AC showable) = "A or C: " ++ show showable
showAC (B i) = "B: " ++ show i
No creo que esto sea posible en haskell. Sin embargo, hay algunas alternativas:
Factoriza el código común con un enlace where
Esto no tiene mucho sentido en su ejemplo, pero es útil si tiene más código en el cuerpo de la expresión del caso:
combine o1 o2 = case (o1,o2) of
(Valid, Invalid) -> handleInvalid
(Invalid, Valid) -> handleInvalid
...
where
handleInvalid = ...
Usa patrones comodín
En mi experiencia, no sucede tan a menudo que desee utilizar dos o patrones en una coincidencia de patrón. En este caso, puede manejar todos los casos "buenos" y usar un patrón de comodín para el resto:
combine o1 o2 = case (o1,o2) of
(Valid, Valid) -> Valid -- This is the only valid case
_ -> Invalid -- All other cases are invalid
Esto tiene la desventaja de que pasa por alto el verificador de exhaustividad y que no puede usar patrones comodín para otros propósitos.
Usa guardias y ==
Si los tipos con los que desea hacer coincidencias son similares a una enumeración, podría considerar crear una instancia de Eq
. Entonces puedes usar ==
y ||
para hacer coincidir varios constructores en una guardia:
combine o1 o2
| o1 == Invalid && o2 == Valid || o1 == Valid && o2 == Invalid = Invalid
| ...
Estoy de acuerdo en que esto no se ve tan bien y también tiene la desventaja de pasar por alto el comprobador de exhaustividad y no le advierte si los patrones se superponen, así que no lo recomendaría.
Puede usar este quasiquoter http://hackage.haskell.org/package/OrPatterns . Tu ejemplo se traduce en algo como:
let combine o1 o2 = case (o1, o2) of
[o| (Valid, Invalid) | (Invalid, Valid ) |] -> Invalid
_ -> ...