Идиоматический способ написать firstRightOrLefts в Haskell?

У меня есть следующий метод:

firstRightOrLefts :: [Either b a] -> Either [b] a
firstRightOrLefts eithers = 
   case partitionEithers eithers of
      (_,  (x : _)) -> Right x
      (xs, _)       -> Left xs

Что меня беспокоит, так это уродливое сопоставление с образцом, и мне было интересно, есть ли более идиоматический способ написать этот метод. Идея состоит в том, что у меня есть куча вычислений, которые могут возвращать оба значения, и я просто хочу получить первый результат или все сообщения об ошибках. Возможно, я использую неправильную структуру данных. Возможно, для этой задачи лучше подойдет монада Writer. Я действительно не уверен на данный момент. Спасибо за любую помощь!


person Robert Massaioli    schedule 05.11.2014    source источник


Ответы (2)


Обратное соглашение на самом деле является просто определением монады для Either, и определение для sequence подходит для этого:

ghci> :t sequence :: [Either a b] -> Either a [b]
sequence :: [Either a b] -> Either a [b]
  :: [Either a b] -> Either a [b]

Поэтому, чтобы применить это к вашему случаю, нам понадобится функция flipEither:

firstRightOrLefts = fe . sequence . map fe
    where fe (Left a) = Right a
          fe (Right b) = Left b
person CR Drost    schedule 05.11.2014
comment
Это на самом деле блестящий ответ, он простой и лаконичный. Оставляю его открытым еще на один день на тот случай, если есть более простой ответ, но в остальном я приму это как ответ. - person Robert Massaioli; 06.11.2014
comment
Я имею в виду, вы также можете написать свой как firstRightOrLefts = go [] where go acc e = case e of [] -> Left (reverse acc); (Right r):es -> Right r; (Left l):es -> go (l:acc) es, если хотите. Что мне действительно нравится в приведенном выше ответе, так это то, что он показывает, что вы, вероятно, используете монаду Либо (по крайней мере, в этой операции) способом, противоположным тому, как это обычно делают люди: поскольку вы хотите накапливать ошибки и останавливаться на первом успехе , ваш вариант использования перевернут по сравнению с людьми, которые хотят остановиться на первой же ошибке. Это, вероятно, не имеет значения, но если вы пишете парсер, это полезно знать! - person CR Drost; 06.11.2014
comment
Вы также можете написать fe = either Right Left. - person Ørjan Johansen; 06.11.2014

Экземпляр MonadPlus для Except ведет себя следующим образом:

import Control.Monad
import Control.Monad.Trans.Except

firstRightOrLefts :: [Either e a] -> Either [e] a
firstRightOrLefts = runExcept . msum . fmap (withExcept (:[]) . except)
person danidiaz    schedule 05.11.2014
comment
Это очень мило. Тем более, что он использует стандартные операторы для выполнения работы. Я собираюсь следить за типами дальше, чтобы увидеть, как он делает то, что делает. - person Robert Massaioli; 06.11.2014