0

我正在研究一组 Data.BSON.Document 结构,将每个结构转换为用户数据结构(我定义了用户)。拆包的功能非常简单:

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

但是,问题在于它实际上似乎并没有在 Either 上下文中出错。以下是一些示例运行:

*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",而是得到一个完整的例外。为什么会发生这种情况,而不是存储在 Either 数据结构中的错误?

4

3 回答 3

2

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"
于 2012-11-25T21:23:06.490 回答
1

根据我可以从这里找到的信息,这里,以及其他一般的谷歌搜索...... Either monad 的实例没有实现fail。猜测一下,这就是为什么我得到一个例外而不是Left。我写了这个小测试来演示:

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?

于 2012-11-26T03:43:31.087 回答
0

如果你想要你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)
于 2012-11-26T03:02:43.710 回答