En Haskell, puedo usar el tipo a -> Maybe b
para modelar una función que devuelve un valor de tipo b
o no devuelve nada (falla).
Si tengo tipos a1, ..., a(n+1)
y funciones f1, ..., fn
, con fi :: ai -> Maybe a(i+1)
for all i
, 1 <= i <= n
puedo encadenar las funciones usando el >>=
operador de la Maybe
mónada y escribir:
f1 x >>= f2 >>= f3 >>=... >>= fn
El >>=
operador se asegura de que cada función se aplique siempre que su predecesor haya devuelto un valor significativo. Tan pronto como falla una función en la cadena, la cadena completa falla (retorna Nothing
) y no se evalúan otras funciones en la cadena.
Tengo un patrón algo similar en el que quiero probar varias funciones en la misma entrada y regresar tan pronto como una función tenga éxito . Si todas las funciones fallan (retorno Nothing
), todo el cálculo debería fallar. Más precisamente, tengo funciones f1, ..., fn :: a -> Maybe b
y defino la función
tryFunctions :: [a -> Maybe b] -> a -> Maybe b
tryFunctions [] _ = Nothing
tryFunctions (f : fs) x = case f x of
Nothing -> tryFunctions fs x
r@(Just _) -> r
En cierto sentido, esto es dual para la Maybe
mónada en que un cálculo se detiene en el primer éxito en lugar de en el primer fracaso.
Por supuesto, puedo usar la función que he escrito anteriormente, pero me preguntaba si hay una forma mejor, bien establecida e idiomática de expresar este patrón en Haskell.
return f1 ?? f2 ?? f3 ?? DefaultValue;
Alternative
es el operador de infijo de símbolo<|>
y se define en términos de un monoideRespuestas:
Dado un conjunto cerrado (número fijo de elementos)
S
con elementos{a..z}
y un operador binario*
:Hay un único elemento de identidad
i
tal que:forall x in S: i * x = x = x * i
El operador es asociativo de manera que:
forall a, b, c in S: a * (b * c) = (a * b) * c
Tienes un monoide.
Ahora, dado cualquier monoide, puede definir una función binaria
f
como:Lo que esto significa es que, por ejemplo, el
Maybe
monoide (Nothing
es el elemento de identidad indicado anteriormente comoi
):Sorprendentemente, no puedo encontrar esta función precisa en las bibliotecas predeterminadas, lo que probablemente se deba a mi propia inexperiencia. Si alguien más puede ser voluntario en esto, lo agradecería sinceramente.
Aquí está la implementación que deduje de la mano del ejemplo anterior:
Ejemplo:
Entonces, si desea utilizar una lista de funciones como entrada ...
ejemplo:
fuente
<|>
trata la identidad de un monoide de una manera especial. ¿No podría uno elegir un elemento arbitrario de un conjunto arbitrario para desempeñar ese papel especial? ¿Por qué el conjunto tiene que ser un monoide y el elemento especial es<|>
su identidad?<|>
no confía en monoide y tengo todo esto mezclado. Se basa enAlternative
typeclass. Para estar seguro - Estoy mirando mi propia respuesta y darse cuenta de que no está del todo bien como[1,2] <|> [3]
da la inesperada[1,2,3]
por lo que todo lo relacionado con el uso de la clase de tipo monoid para identificar una identidad que es correcto - y la otra clave es la asociatividad es necesario para obtener el comportamiento esperado , tal vezAlternative
no da el comportamiento que pensé de la mano ...f
para ver por qué la asociatividad es una necesidad.fuente
Maybe
... Mi solución está limitadaEq
, de alguna manera siento que a los dos nos falta algo disponible enMonoid
general ...either
?Monoid
que cualquier cosa con un elemento de identidad pueda tener un conjunto de 2 funciones (creo que este es un tipo de campo binario), donde una de las funciones elige identidad sobre todas las demás, y la otra función siempre elige El elemento de no identidad. Simplemente no sé cómo hacer esto sinEq
saber qué valor esidentity
o no ... obviamente en los monoides aditivos o multiplicativos obtienes la función de escalera por defecto (siempre elige elementos que no sean de identidad usando la función binaria de monoides)foldMap
se puede usar en lugar demconcat . map
Esto se parece mucho a reemplazar el fracaso por una lista de éxitos
Estás hablando en
Maybe a
lugar de hacerlo[a]
, pero en realidad son muy similares: podemos pensarMaybe a
que son como[a]
, excepto que puede contener como máximo un elemento (es decir,Nothing ~= []
yJust x ~= [x]
).En el caso de las listas,
tryFunctions
sería muy simple: aplique todas las funciones al argumento dado y luego concatene todos los resultados juntos.concatMap
hará esto muy bien:De esta manera, podemos ver que el
<|>
operador paraMaybe
actúa como concatenación para 'listas con como máximo un elemento'.fuente
En el espíritu de Conal, divídalo en operaciones más pequeñas y simples.
En este caso,
asum
desdeData.Foldable
hace la parte principal.Alternativamente, a la respuesta de Jimmy Hoffa puede usar la
Monoid
instancia,(->)
pero luego necesita unaMonoid
instanciaMaybe
y la estándar no hace lo que desea. ¿QuieresFirst
partirData.Monoid
.(O
mconcat
para una versión anterior y más especializada defold
).fuente