Question

I'm trying to understand how to apply the Maybe-idiom from Haskel.. I'm reading http://en.wikibooks.org/wiki/Haskell/Understanding_monads/Maybe which shows that a lookup in a dictionary may return a Maybe and that this value propates through the >>= operator.

The example from the URL:

If we then wanted to use the result from the governmental database lookup in a third lookup (say we want to look up their registration number to see if they owe any car tax), then we could extend our getRegistrationNumber function:

getTaxOwed :: String       -- their name
           -> Maybe Double -- the amount of tax they owe
getTaxOwed name = 
  lookup name phonebook >>=
    (\number -> lookup number governmentalDatabase) >>=
      (\registration -> lookup registration taxDatabase)

Or, using the do-block style:

getTaxOwed name = do
  number       <- lookup name phonebook
  registration <- lookup number governmentalDatabase
  lookup registration taxDatabase

Question:

How do I deal with error handling? I think most code will benefit from telling where things went wrong. Rather than just reporting "couldn't find John Doe in either phonebook or governmentalDatabase" it should report which ressource had problems.

Was it helpful?

Solution

You could use the monad instance for Either String, which is essentially defined as

instance Monad (Either String) where                                             
  fail msg = Left msg                                                            
  return x = Right x                                                             

  Left msg >>= k = Left msg                                                      
  Right x  >>= k = k x

(The actual definition is a bit more involved.)

If we then define dictionaries as pairs consisting of a label and a lookup table

type Dict a b = (String, [(a, b)])

phonebook' :: Dict String Int
phonebook' = ("phone book", phonebook)

governmentalDatabase' :: Dict Int Int
governmentalDatabase' = ("governmental database", governmentalDatabase)

taxDatabase' :: Dict Int Double
taxDatabase' = ("tax database", taxDatabase)

where phonebook, governmentalDatabase, and taxDatabase are as you had them defined before, we can use an alternative monadic lookup function that returns its result in the Either String-monad:

lookup' :: (Eq a, Show a) => a -> Dict a b -> Either String b
lookup' key (descr, table) = case lookup key table of
  Nothing  -> Left ("couldn't find " ++ show key ++ " in " ++ descr)
  Just val -> Right val

Illustrating the power of monads, the only thing that now needs to change in your client function is the type signature:

getTaxOwed :: String               -- their name                                 
           -> Either String Double -- either an error message                    
                                   -- or the amount of tax they owe              
getTaxOwed name = do
  number       <- lookup' name phonebook'
  registration <- lookup' number governmentalDatabase'
  lookup' registration taxDatabase'

Running this function on an unknown name gives:

> getTaxOwed "Joe"
Left "couldn't find \"Joe\" in phone book"

OTHER TIPS

The Maybe data type has only the value "Nothing" for signaling an error. If you want to return a specific error message i suggest the data type "Either", which can return either a "Left a" or "Right a" value. Read more about how to use that at http://learnyouahaskell.com/for-a-few-monads-more#error

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top