Слияние / добавление Jasts в Haskell - PullRequest
9 голосов
/ 24 января 2012

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

Ответы [ 4 ]

16 голосов
/ 24 января 2012

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

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

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

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

3 голосов
/ 25 января 2012

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

fmap join $ sequence [Just[1],Just[2],Just[3]]
-- Just [1,2,3]
3 голосов
/ 24 января 2012

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

mergeJust a b = do
    a' <- a
    b' <- b
    return (a' ++ b')
1 голос
/ 12 августа 2012

Поскольку это не упоминалось в других решениях, я скажу это здесь.На мой взгляд, самый простой способ выполнить вашу задачу - это использовать <> (или 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
...