如何使用 Haskell 的 BSON 模块捕获简单的错误?



我有一组 Data.BSON.Document 结构,我正在挖掘它们,将每个结构转换为用户数据结构(我定义了 User)。 解包的功能非常简单:

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" =: "savanni@nowhere.com", "auth" =: "random_token"]
Right (User {name = "Savanni", email = "savanni@nowhere.com", token = "random_token"})
*DB> docToUser ["name" =: "Savanni", "email" =: "savanni@nowhere.com", "a" =: "random_token"]
*** Exception: expected "auth" in [ name: "Savanni", email: "savanni@nowhere.com", a: "random_token"]

因此,第一次运行返回包装在 Right 构造函数中的用户。 第二个我期望诸如 Left "field not found" ,但得到了一个完整的例外。 为什么会发生这种情况,而不是存储在任一数据结构中的错误?

look表示

"未找到"通过一元fail原语。返回类型Either这一事实无关紧要。您无法在do表达式中处理此故障;你必须写这样的东西:

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"

根据我能从这里、这里和一般其他谷歌搜索中找到的信息......任一 monad 的实例没有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"

我想这可能需要相当多的改进,尤其是在检测和报告哪些字段失败时。 但是,这似乎非常接近答案。

我认为,鉴于此,这个问题是没有标准(任一)monad实例的副本?

如果您希望lookEither上下文中:

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)

最新更新