2012-01-24 14 views
9

Estoy tratando de hacer lo que debe ser deslumbrantemente obvio en Haskell, que va desde Just [1] y Just [2] hasta Just [1, 2]. Sin embargo, no puedo encontrar nada en línea ya que sigo encontrando páginas relacionadas pero inútiles. Entonces, ¿cómo lo logras?Fusionando/Anexando Justs en Haskell

Respuesta

15

Puede utilizar liftA2 (++):

liftA2 (++) :: Maybe [a] -> Maybe [a] -> Maybe [a] 

liftA2 simplemente levanta una función binaria en un Applicative. Applicative s fueron diseñados para levantar funciones de argumentos arbitrarios en un contexto, por lo que son perfectos para esto. En este caso, el Applicative que estamos usando es Maybe. Para ver cómo funciona esto, podemos mirar a la definición:

liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c 
liftA2 f a b = f <$> a <*> b 

(<$>) simplemente levanta ninguna función en los valores puros a un funcionamiento en el interior f: (a -> b) -> f a -> f b. (Es sólo un alias para fmap, si está familiarizado con Functor s.) Para Maybe:

_ <$> Nothing = Nothing 
f <$> Just x = Just (f x) 

(<*>) es un poco más complicado: se aplica una función dentro de f a un valor dentro de f: f (a -> b) -> f a -> f b. Para Maybe:

Just f <*> Just x = Just (f x) 
_ <*> _ = Nothing 

(De hecho, f <$> x es lo mismo que pure f <*> x, que es Just f <*> x para Maybe.)

Por lo tanto, podemos ampliar la definición de liftA2 (++):

liftA2 (++) a b = (++) <$> a <*> b 

-- expand (<$>) 
liftA2 (++) (Just xs) b = Just (xs ++) <*> b 
liftA2 (++) _ _ = Nothing 

-- expand (<*>) 
liftA2 (++) (Just xs) (Just ys) = Just (xs ++ ys) 
liftA2 (++) _ _ = Nothing 

De hecho, podemos usar estos operadores para levantar una función de cualquier cantidad de argumentos en cualquier Applicative, j ust siguiendo el patrón de liftA2. Esto se llama estilo aplicativo, y es muy común en el código Haskell idiomático. En este caso, podría ser incluso más idiomático usarlo directamente escribiendo (++) <$> a <*> b, si a y b ya son variables. (Por otro lado, si está parcialmente su aplicación - por ejemplo, para pasar a una función de orden superior - a continuación, liftA2 (++) es preferible.)

Cada Monad es un Applicative, por lo que si alguna vez se encuentra tratando de "levantar" una función en un contexto, Applicative es probablemente lo que estás buscando.

+0

Awesome :) Gracias, me has salvado arrancándome el pelo. No suponga que conoce un equivalente para '[2]' y 'Just [3]' -> 'Just [2, 3]' ¿verdad? :) –

+4

@DeanBarnes: '(2 :) <$> Simplemente [3]' –

+0

¡Respuesta fantástica, gracias @ehird! Esta es básicamente mi referencia a partir de ahora :) –

3

mientras que la respuesta de @ ehird es grande, habría utilizado una solución noobish en la forma:

mergeJust a b = do 
    a' <- a 
    b' <- b 
    return (a' ++ b') 
+2

+1 incluso noobs, equipados con herramientas simples, pueden resolver este problema. También podría escribir lo mismo que una comprensión de mónada: '[a '++ b' | a '<- a, b' <- b] ' –

3

para expandir la solución a una lista de Just s, se puede usar

fmap join $ sequence [Just[1],Just[2],Just[3]] 
-- Just [1,2,3] 
1

Como no fue mencionado en otras soluciones, lo diré aquí. La forma más sencilla de realizar su tarea, en mi opinión, es usar <> (o mappend) desde Data.Monoid.

import Data.Monoid 

Just [1,2] <> Just [7,8] == Just [1,2,7,8] 

Sin embargo, tenga en cuenta que esta solución, a diferencia de la solución aplicativa de ehird, se provoque un cortocircuito en Nothing valores.

Just [1,2] <> Nothing ---> Just [1,2] 
--However 
(++) <$> Just [1,2] <*> Nothing ---> Nothing 
+0

A veces este será el comportamiento adecuado, a veces no. –