我有一组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数据结构中的错误?
答案 0 :(得分:2)
look
通过monadic 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"
答案 1 :(得分:1)
基于我可以从here,here以及其他一般Google搜索中找到的信息...... Either monad的实例不会拥有 a {{ 1}}实施。猜测一下,这就是为什么我得到一个异常而不是Left。我写了这个小测试来证明:
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"
确实没有返回任何内容。看起来我的docToUser转换的正确方法更类似于:
fail :: String -> Maybe String
我认为这可以进行相当多的改进,特别是在检测和报告哪些字段失败时。但是,这似乎非常接近答案。
我认为,鉴于此,这个问题与Is there no standard (Either a) monad instance?
重复答案 2 :(得分:0)
如果您想在look
上下文中使用Either
:
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)