2012-01-24 3 views
9

Я пытаюсь сделать то, что должно быть ослепительно очевидным в Haskell, которое идет от Just [1] и Just [2] до Just [1, 2]. Однако я не могу найти что-либо в Интернете, поскольку я продолжаю находить связанные, но бесполезные страницы. Итак, как вы это достигаете?Слияния/добавления Justs в Haskell

ответ

15

Вы можете использовать liftA2 (++):

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

liftA2 просто поднимает бинарную функцию в Applicative. Applicative s были предназначены для подъема функций произвольных аргументов в контексте, поэтому они идеально подходят для этого. В этом случае используется Applicative: Maybe. Чтобы увидеть, как это работает, мы можем посмотреть на определение:

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

(<$>) просто поднимает любую функцию на чистых значений одной операционной внутри f: (a -> b) -> f a -> f b. (Это просто псевдоним для fmap, если вы знакомы с Functor с.) Для Maybe:

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

(<*>) немного сложнее: он применяет функцию внутри f к значению внутри f: f (a -> b) -> f a -> f b. Для Maybe:

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

(На самом деле, f <$> x это то же самое, как pure f <*> x, который Just f <*> x для Maybe.)

Таким образом, мы можем расширить определение 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 

Действительно, мы можем использовать эти операторы для подъема функции любой количество аргументов в любые Applicative, j ust, следуя схеме liftA2. Это называется аппликативный стиль и очень распространен в идиоматическом коде Haskell. В этом случае может быть даже более идиоматично использовать его непосредственно, написав (++) <$> a <*> b, если a и b уже являются переменными. (С другой стороны, если вы частично применить его - скажем, передать его функции высшего порядка - то liftA2 (++) предпочтительнее.)

Каждый Monad является Applicative, так что если вы когда-нибудь найти себя, пытаясь «поднять» функцию в контекст, Applicative - это, вероятно, то, что вы ищете.

+0

Awesome :) Спасибо, ты спас меня, вырвав мои волосы. Не думайте, что вы знаете эквивалент для '[2]' и 'Just [3]' -> 'Just [2, 3]' вы? :) –

+4

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

+0

Фантастический ответ, спасибо @ehird! Это в основном моя ссылка с этого момента :) –

3

в то время как @ ehird Ответим велик, я бы использовал noobish решение в виде:

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

+1 даже noobs, оснащенный простыми инструментами, может решить эту проблему. Вы также можете написать то же, что и монадское понимание: '[a '++ b' | a '<- a, b' <- b] ' –

3

Чтобы расширить решение списка Just с, вы могли бы использовать

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

Поскольку он не упоминался в других решениях, я скажу это здесь. Самый простой способ выполнить вашу задачу, на мой взгляд, - использовать <> (или mappend) от Data.Monoid.

import Data.Monoid 

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

Однако следует отметить, что это решение, в отличии от аппликативного раствора ehird, в не будет короткое замыкание на Nothing значений.

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

Иногда это будет правильное поведение, иногда нет. –

Смежные вопросы