public
Created

Alternative Applicative instance for Either that collects multiple Left values

  • Download Gist
CollectLeft.hs
Haskell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
import Data.Monoid
import Control.Applicative
 
newtype CollectLeft es a = CollectLeft (Either es a)
deriving (Show, Eq)
 
-- Alternative applicative instance for Either with a Monoid on the Left
-- If everything is Right, then it acts like the normal instance
-- Instead of resulting in the first Left, the values inside all Left are
-- concatenated (any Monoid)
instance (Monoid es) => Applicative (CollectLeft es) where
pure = CollectLeft . Right
CollectLeft (Right f) <*> CollectLeft (Right b) = CollectLeft $ Right (f b)
CollectLeft (Right _) <*> CollectLeft (Left es) = CollectLeft (Left es)
CollectLeft (Left es) <*> CollectLeft (Right _) = CollectLeft (Left es)
CollectLeft (Left es) <*> CollectLeft (Left ds) = CollectLeft $ Left (es `mappend` ds)
 
-- Every Applicative is a Functor
instance (Monoid es) => Functor (CollectLeft es) where
fmap f x = pure f <*> x
 
-- lift an Either into this alternate instance
-- Instead of taking an Either with a Monoid on the Left,
-- this actually takes any Either and pure's it into a Monoid
-- (must be both Monoid and Applicative so values can be injected)
collectEither :: (Monoid (es a), Applicative es) => Either a b -> CollectLeft (es a) b
collectEither (Left e) = CollectLeft (Left $ pure e)
collectEither (Right a) = CollectLeft (Right a)
 
-- Extractor because we had to use a newtype
runCollectLeft :: CollectLeft es a -> Either es a
runCollectLeft (CollectLeft either) = either

You can do

newtype CollectLeft es a = CollectLeft { runCollectLeft :: Either es a }

instead of defining the extractor

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.