22

Somewhere in my app I receive an Either ParserError MyParseResult from Parsec. Downstream this result gets some other parsing done over using other libs. During that second phase of parsing there also may occur some kind of error which I would like to pass as a Left String, but for that I need to convert the result from Parsec to String too. To achieve that I need a function which will allow me to map over a Left with a show function.

The mapping function I'm thinking of looks something like this:

mapLeft :: (a -> b) -> Either a c -> Either b c
mapLeft f (Left x) = Left $ f x
mapLeft _ x = x

But I was quite surprised not to find anything matching on hackage db. So now I'm having doubts whether I'm using a correct approach to my problem.

Why isn't there such a function in standard lib? What is wrong with my approach?

Nikita Volkov
  • 41,289
  • 10
  • 85
  • 162
  • 8
    You can't use `mapLeft _ x = x`, that must be `mapLeft _ (Right x) = Right x`, the argument and result have different types. – Daniel Fischer Nov 22 '12 at 00:01

4 Answers4

34

We have such a function in the standard libraries,

Control.Arrow.left :: a b c -> a (Either b d) (Either c d)

is the generalisation to arbitrary Arrows. Substitute (->) for a and apply it infix, to get the specialisation

left :: (b -> c) -> Either b d -> Either c d

There is nothing wrong with your approach in principle, it's a sensible way to handle the situation.

Daniel Fischer
  • 174,737
  • 16
  • 293
  • 422
20

Another option is to use Bifunctor instance of Either. Then you have

first :: (a -> b) -> Either a c -> Either b c

(Also Bifunctor can be used to traverse over the first part of (a,b).)

Chris Stryczynski
  • 19,899
  • 28
  • 104
  • 198
Petr
  • 60,177
  • 8
  • 136
  • 295
  • Seeing `Either` as a `Bifunctor` in a context that, as stated, what I needed was to "map", seems very reasonable. Thank you! I wonder why Hoogle doesn't include any of these functions in its results. – Nikita Volkov Nov 22 '12 at 15:37
  • 3
    @NikitaVolkov I was curious about that too, and I found out that [Hoogle searches: array, arrows, base, bytestring, Cabal, cgi, containers, directory, filepath, haskell-src, HUnit, mtl, old-locale, old-time, packedstring, parallel, parsec, pretty, process, QuickCheck, random, stm, template-haskell, time, xhtml](http://www.haskell.org/haskellwiki/Hoogle#Scope_of_Web_Searches). Probably one would have to create a full, local database to search all Hackage libraries. – Petr Nov 22 '12 at 18:20
14

This can be done easily with lens:

import Control.Lens

over _Left (+1) $ Left 10   => Left 11
over _Left (+1) $ Right 10  => Right 10
over _Right (+1) $ Right 10 => Right 11
John Wiegley
  • 6,340
  • 1
  • 12
  • 19
3

Another simple option is mapLeft in Data.Either.Combinators:

mapLeft :: (a -> c) -> Either a b -> Either c b
Xiaokui Shu
  • 372
  • 1
  • 8