Reputation: 703
I am learning Haskell, and having a great time. One of the things I especially enjoy is using the monad error types to propagate error conditions behind the scene in fmap or >>=. For example, in this code, I am using hoauth2 for an authenticated connection. It defines OAuth2Result using Either...
type OAuth2Result a = Either ByteString a -- from OAuth2
getEntries :: Manager -> AccessToken -> IO(OAuth2Result [Entry])
-- code omitted
filterResults :: [Entry] -> OAuth2Result [Entry]
filterResults = return $ filter hasUrl
printEntries :: [Entry] -> IO() -- What type can I use here?
printEntries Left l = -- code omitted
printEntries Right r = -- code omitted
main = do
entriesResult <- getEntries mgr token -- This is an OAuth2Result
let filtered = entriesResult >>= filterResults
printEntries filtered
The problem that I am having is when a function has IO like printEntries. In this case, I have to explicitly pattern match to do the error handling. I would sure love to be able to hide it somehow as I did with the filterResults call.
How can I do this?
Thanks!
Upvotes: 0
Views: 193
Reputation: 52039
Here's how to do it with runEitherT
which requires some lifting and hoisting to get the types right:
import Control.Monad
import Control.Monad.Trans
import Control.Error
import Data.List (isInfixOf)
type Entry = String
type Result a = Either String a
sampleEntries = [ "good 1", "good 2", "good 3", "bad 4", "good 5", "bad 6", "good 7" ]
getEntries :: Int -> IO (Result [Entry])
getEntries n = if n >= 0 && n <= length sampleEntries
then return $ Right $ take n sampleEntries
else return $ Left $ "invalid n: " ++ show n
filterEntries :: [Entry] -> Result [Entry]
filterEntries ents = if all isGood ents
then Right $ ents
else Left "found a bad entry"
where isGood str = isInfixOf "good" str
printEntries :: [Entry] -> IO ()
printEntries ents = forM_ (zip [1..] ents) $ \(i,e) -> print (i,e)
doit n = do
ents <- (lift $ getEntries n) >>= hoistEither
filtered <- hoistEither $ filterEntries ents
lift $ printEntries filtered
main n = do result <- runEitherT $ doit n
case result of
Left e -> putStrLn $ "Error: " ++ e
Right _ -> putStrLn $ "no errors"
Note the following behavior:
main 100
fails because getEntries
returns an errormain 4
fails because filterEntries
returns an errormain 3
succeedsUpvotes: 1