2012-11-25 18 views
0

我有一組Data.BSON.Document結構,我正在挖掘,將每個轉換爲一個用戶數據結構(我定義的用戶)。做拆包功能是非常簡單的:如何用Haskell的BSON模塊捕捉簡單的錯誤?

docToUser :: Document -> Either String User 
docToUser u = do 
    name <- look "name" u >>= \(String t) -> return $ unpack t 
    email <- look "email" u >>= \(String t) -> return $ unpack t 
    token <- look "auth" u >>= \(String t) -> return $ unpack t 
    Right $ User name email token 

美中不足,雖然是實際上它並不似乎在無論哪種情況下錯誤的。下面是一些例子運行:

*DB> docToUser ["name" =: "Savanni", "email" =: "[email protected]", "auth" =: "random_token"] 
Right (User {name = "Savanni", email = "[email protected]", token = "random_token"}) 
*DB> docToUser ["name" =: "Savanni", "email" =: "[email protected]", "a" =: "random_token"] 
*** Exception: expected "auth" in [ name: "Savanni", email: "[email protected]", a: "random_token"] 

所以,在第一次運行返回包裹Right構造函數中的用戶。第二個我期望的東西,如Left "field not found",但相反得到一個完整的例外。爲什麼會發生這種情況而不是存儲在任一數據結構中的錯誤?

回答

1

基於信息,我可以從here發現,here和其他一般的谷歌搜索左右...實例爲單元monads並沒有 a fail實現。猜測,這就是爲什麼我得到一個異常,而不是左。我寫了這個小測試來證明:

eitherMonad :: String -> Either String String 
eitherMonad val = do 
    if val == "abcd" 
     then fail "val is abcd" 
     else return "val is something else" 

*DB> eitherMonad "abcd" 
*** Exception: val is abcd 
*DB> eitherMonad "efgh" 
Right "val is something else" 

在另一方面,fail :: String -> Maybe String確實返回Nothing。這樣看來,做我的docToUser轉換的正確方法是什麼這個更類似於:

docToUser :: Document -> Either String User 
docToUser u = do 
    let name = look "name" u :: Maybe Value 
    let email = look "email" u :: Maybe Value 
    let token = look "auth" u :: Maybe Value 
    case (name, email, token) of 
     (Just (String n), Just (String e), Just (String t)) -> Right $ User (unpack n) (unpack e) (unpack t) 
     (Nothing, _, _) -> Left "username not found" 
     (Just (String n), Nothing, _) -> Left "email not found" 
     (Just (String n), Just (String e), Nothing) -> Left "auth token not found" 
     otherwise -> Left "Something else broke" 

我猜想,可能需要相當多的改進,特別是在檢測和哪些領域失敗的報告。但是,這似乎與答案非常接近。

我想,因爲,這個問題是Is there no standard (Either a) monad instance?

+1

請注意,在後面的所有情況下,您不需要放入'Just(String ..)'行,在之前的情況下,'Nothing'將會捕獲它們,即'case .. of ...; (Nothing,_,_) - > ...; (_,Nothing,_) - > ...; (_,_,Nothing) - > ...'工作得很好。 – huon

2

look指示通過單元fail原語的「未找到」。您的退貨類型爲Either的事實無關緊要。您無法在do表達式中處理此故障;你必須寫是這樣的:如果你在Either方面希望你look

unpackUser u = case (look "name" u, look "email" u, look "auth") of 
    (Just (String name), Just (String email), Just (String token)) -> Right $ User (unpack name) (unpack email) (unpack token) 
    _ -> Left $ "Missing required fields" 
+1

重複輸出由爲'error'一個電話,這是一樣的'在IO fail'造成的,而是'失敗= Left'在任一monad中。 – huon

+0

這是無關緊要的。 'look'中的monad與'unpackUser'使用'Either'無關。在我的例子中,'look'使用'Maybe'。它也可以使用'Either'。 –

+0

在這個問題中的解包是明確地在任一monad中,所以'失敗'應該在它內部發生,也就是說,留下而不是例外。 – huon

0

docToUser = do 
    String name <- look "name" u 
    String email <- look "email" u 
    String token <- look "token" u 
    return $ User (unpack name) (unpack email) (unpack token)