10

I have the following method:

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

What bothers me is the ugly pattern matching and I was wondering if there was a more idiomatic way to write this method. The idea is that I have a bunch of computations that can return Eithers and I just want to get the first result or all of the error messages. Maybe I'm using the wrong data structure. Maybe the Writer monad would be better suited to this task. I'm really not sure at this point. Cheers for any help!

Robert Massaioli
  • 13,379
  • 7
  • 57
  • 73

2 Answers2

18

The reverse convention is actually just the monad definition for Either and the definition for sequence is adequate for this:

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

To actually apply this to your case we'd therefore need a function flipEither:

firstRightOrLefts = fe . sequence . map fe
    where fe (Left a) = Right a
          fe (Right b) = Left b
amalloy
  • 89,153
  • 8
  • 140
  • 205
CR Drost
  • 9,637
  • 1
  • 25
  • 36
  • That is actually a brilliant answer, it's simple and concise. Leaving it open for one more day just in case there is a simpler answer out there but otherwise I'll accept this as the answer. – Robert Massaioli Nov 05 '14 at 21:50
  • 2
    I mean, you can also write yours as `firstRightOrLefts = go [] where go acc e = case e of [] -> Left (reverse acc); (Right r):es -> Right r; (Left l):es -> go (l:acc) es` if you want. What I really like about the above answer is that it reveals that you're probably using the Either monad (at least in this operation) in a way opposite to how people normally do: since you want to accumulate errors and stop on the first success, your use-case is flipped compared to people who want to stop on the first error. It probably doesn't matter but if you're writing a parser, that's useful to know! – CR Drost Nov 05 '14 at 22:07
  • 8
    You can also write `fe = either Right Left`. – Ørjan Johansen Nov 05 '14 at 23:03
9

The MonadPlus instance for Except has this behaviour:

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

firstRightOrLefts :: [Either e a] -> Either [e] a
firstRightOrLefts = runExcept . msum . fmap (withExcept (:[]) . except)
danidiaz
  • 26,936
  • 4
  • 45
  • 95
  • That's pretty nice. Especially since it uses standard operators to do the work. I'm going to follow the types further to see how it does what it does. – Robert Massaioli Nov 05 '14 at 21:50