错误处理和monad?

时间:2012-08-13 09:17:24

标签: haskell error-handling monads maybe

我正在尝试理解如何应用来自Haskel的Maybe-idiom ..我正在阅读http://en.wikibooks.org/wiki/Haskell/Understanding_monads/Maybe,这表明字典中的查找可能返回Maybe并且此值支持通过>>=运营商。

网址中的示例:

如果我们想在第三次查询中使用政府数据库查询的结果(比如我们想查看他们的注册号以查看他们是否欠任何汽车税),那么我们可以扩展我们的getRegistrationNumber函数:

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)

或者,使用do-block样式:

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

问题:

如何处理错误处理?我认为大多数代码都会从告诉出错的地方中受益。它不应仅报告“无法在电话簿或政府数据库中找到John Doe”,而应报告哪个资源存在问题。

2 个答案:

答案 0 :(得分:8)

你可以使用Either String的monad实例,它基本上被定义为

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

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

(实际定义涉及更多。)

如果我们然后将字典定义为由标签和查找表组成的对

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)

其中phonebookgovernmentalDatabasetaxDatabase与之前定义的一样,我们可以使用替代monadic查找函数,在Either String中返回其结果 - 单子:

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

说明monad的强大功能,现在唯一需要在客户端函数中更改的是类型签名:

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'

在未知名称上运行此函数会产生:

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

答案 1 :(得分:5)

Maybe数据类型只有值“Nothing”用于表示错误。如果要返回特定的错误消息,我建议使用数据类型“Either”,它可以返回“Left a”或“Right a”值。详细了解如何在http://learnyouahaskell.com/for-a-few-monads-more#error

中使用该功能