This is more or less a simplified version of the issue I’m trying to understand in my own code. I’m working with functions that are polymorphic in the Failure class from this package.
{-# LANGUAGE FlexibleContexts #-}
import Data.Maybe
import Data.Either
import Control.Failure
data FookError = FookError deriving Show
fook :: (Failure FookError m)=> Int -> m Int
fook = undefined
fooks :: (Failure FookError m)=> Int -> m Int
-- DOES NOT TYPE-CHECK:
--fooks n = return $ head $ rights $ map fook [1..]
-- OKAY:
fooks n = return $ head $ catMaybes $ map fook [1..]
You can see in the above code, that when I treat the return type of fook as Maybe Int the module compiles fine, however treating it as Either Fook Int fails.
What is going on here?
This is because, in the non-working definition of
fooks,fook‘s type is ambiguous. When you usecatMaybes, it disambiguates asMaybe Int, but when you userights, it can beEither e Intfor anye, and the compiler doesn’t necessarily know which. Sure, by default the onlyFailureinstance forEitherisinstance Failure e (Either e), but there’s nothing stopping you defining, e.g.instance Failure String (Either Int).If you explicitly specify the type by defining
fooksasthen it works fine.
However, I suspect that you’re not doing what you really want here;
fooksnever actually uses the failure capability of the underlying monad; indeed, even if there are no non-failing results, the monadic action still succeeds and returns a value. The value just so happens to be an error, but that’s still probably not what you want 🙂If you want
fooksto try a bunch of individualfooks in turn, and return the first one that succeeds, then something like:should do the trick. The plain
Failureclass by itself offers no way to recover from errors, so you need to requireMonadPlustoo. Note that thefailure FookErrorhere will never actually be used, since[1..]is infinite, but presumably you’re planning to change the definition; say to one that actually usesn🙂Unfortunately, that isn’t all!
Either edoesn’t have aMonadPlusinstance, presumably because there’s no reasonable value ofmzero(although another potential problem is thatmplus (Left e) (Left e')could be eitherLeft eorLeft e').Thankfully, it’s easy to define an instance for our specific type:
You’ll need
{-# LANGUAGE FlexibleInstances #-}at the top of your file to do this.