2016-07-02 2 views
4

Я новичок в Haskell, и я работаю над некоторым кодом Haskell, который использует Either для обработки ошибок. Левый элемент Литера представляет ошибку, в то время как правый элемент представляет собой успешный результат. В коде часто используется аппликативный экземпляр Either, который, как представляется, предназначен для этого варианта использования.Преобразование списка Eithers в Либо со списком в нем

С данным объектом [Either e r], что является самым элегантным способом превратить его в объект типа Either e [r]? Дело в том, что у нас может быть массив возвращаемых значений из некоторых функций, которые мы вызывали, и если какое-либо из этих возвращаемых значений является ошибкой, мы хотим получить эту ошибку и выбросить все остальное. Если у нескольких элементов массива есть ошибка, я предпочел бы получить самую левую ошибку, если это возможно.

Я смог решить эту проблему самостоятельно в коде ниже, написав две функции, одна из которых рекурсивна, но есть ли лучший способ?

type Error = [Char] 

myFunc :: [Either Error a] -> Either Error [a] 
myFunc = myFunc2 [] 

myFunc2 :: [a] -> [Either Error a] -> Either Error [a] 
myFunc2 r ((Left error):rest) = Left error 
myFunc2 r ((Right item):rest) = myFunc2 (r ++ [item]) rest 
myFunc2 r [] = Right r 

main :: IO() 
main = do 
    -- This prints: Right [1, 2, 3] 
    putStrLn (show (myFunc [Right 1, Right 2, Right 3])) 

    -- This prints: Left "division by zero" 
    putStrLn (show (myFunc [Right 1, Left "division by zero", Right 3])) 
+1

В случае, если вы не в курсе, есть удобный сайт под названием [Hoogle] (https://www.haskell.org/hoogle/), где вы можете ввести тип -значение функции, которую вы хотите увидеть, и даст вам предложения о функциях с соответствующими/подобными сигнатурами. К сожалению, '[Либо lr] -> Либо l [r]', похоже, не дает никаких результатов :( – paul

ответ

12

sequence из Control.Monad:

sequence :: (Traversable t, Monad m) => t (m a) -> m (t a) 

как в:

\> import Control.Monad (sequence) 

\> sequence [Right 1, Right 2, Right 3] 
Right [1,2,3] 

\> sequence [Right 1, Left "division by zero", Right 3] 
Left "division by zero" 
+0

Спасибо, это работает! Я просто новичок, поэтому я не уверен, согласен ли этот ответ или тот, который рекомендует 'sequenceA' из Data.Traversable. Каковы преимущества' sequence'? –

+3

@DavidGrayson _default_ реализация такая же, [см. 'sequence = sequenceA' в исходном коде] (http: //hackage.haskell. org/package/base-4.9.0.0/docs/src/Data.Traversable.html # Traversable). Вы будете использовать 'sequenceA', если у вас есть аппликация, которая не является монадой. –

+0

@David Grayson они делают то же самое; они разные функции по историческим причинам https://wiki.haskell.org/Functor-Applicative-Monad_Proposal#Redundant_functions – danidiaz

8

Использование sequenceA из Data.Traversable

+0

Спасибо, это работает! Я просто новичок, поэтому я не уверен, согласен ли этот ответ или тот который рекомендует 'sequence' из Control.Monad. В чем преимущества' sequenceA'? –

+3

'sequence' и' sequenceA' одинаковы для 'Either'. Причина, по которой они оба существуют, является исторической: до того, как предложение «Прикладное приложение» «Прикладное» не было суперклассом «Монады», и многие функции были реализованы дважды, как «traverse» и «mapM». – mariop

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