Create a gist now

Instantly share code, notes, and snippets.

Alternative Applicative instance for Either that collects multiple Left values
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
@yairchu

You can do

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

instead of defining the extractor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment