Recientemente me he encontrado con situaciones en las que necesito pasar una función de predicado a otra función, y con frecuencia la lógica que estoy buscando es esencialmente "¿este valor coincide con este patrón?"
La coincidencia de patrones parece preferirse en declaraciones, do
bloques y comprensiones de listas, pero hay una serie de funciones que toman un predicado a -> Bool
, donde sería muy útil pasar de alguna manera un patrón. Por ejemplo, takeWhile
, until
, find
, span
, etc.
Hasta ahora he estado haciendo \a -> case a of MyCons _ -> True; otherwise -> False
, o escribiendo una función con nombre a la let myPred (MyCons _) = True; myPred _ = False in
pero ambos parecen terriblemente feos y no muy idiomáticos. La forma "obvia" (e incorrecta) sería algo así, \(MyCons _) -> True
pero arroja un error por ser parcial, naturalmente, e incluso entonces parece que debe haber una forma más limpia.
¿Hay una manera más sucinta / limpia de hacer este tipo de cosas? ¿O estoy yendo por cosas completamente equivocadas?
let
cláusula que no le gusta, aunque prefiero lawhere
cláusula equivalente para que esto no sature la definición principal. Por supuesto, si termina necesitando esta utilidad más de una vez, la definiría como una función de nivel superior.let myPred...
estilo es malo , pero se siente mucho más detallado de lo que esperaría para una idea muy simple, lo que me lleva a preguntarme si estoy ladrando el árbol equivocado.maybe :: b -> (a -> b) -> Maybe a -> b
ybool :: a -> a -> Bool -> a
, luego, usarla con funciones productoras de Boole como argumento (s). por ejemplomyCons z f (MyCons x) = f x ; myCons z f _ = z
, luego llamemyCons False (const True) aMyConsValue
. esto es casi lo que escribió, solo tiene un nivel más de "indirección" / "abstracción" a través de argumentos funcionales, integrado en él.Respuestas:
Puede usar la extensión de idioma LambdaCase para usar
\case MyCons _ -> True; _ -> False
, aunque esto no guarda tantos caracteres.Creo que podría escribir una serie de funciones
constructedWith :: (Generic a) => (b -> a) -> a -> Bool
,constructedWith2 :: (Generic a) => (b -> c -> a) -> a -> Bool
pero no soy lo suficientemente competente con Generics para implementarlo sin unas pocas horas de prueba. Intentaré esto y editaré mi respuesta si puedo resolverlo o si es un callejón sin salida.EDITAR: Sí, puedes hacerlo! Aquí hay un enlace a mi código, que lo implementa todo desde cero:
https://repl.it/@lalaithion/ConstructedWith
Sin embargo, usar algo como http://hackage.haskell.org/package/generic-deriving-1.13.1/docs/Generics-Deriving-ConNames.html para todas las tuberías genéricas de código podría ser mejor.
fuente