I have a group of Data.BSON.Document structures that I am digging through, converting each one into a User data structure (I defined User). The function to do the unpacking is pretty straightforward:
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
The catch, though, is that it does not actually seem to error out in an Either context. Here are some example runs:
*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"]
So, the first run returns a user wrapped inside a Right constructor. The second one I expected something such as Left "field not found", but instead get a full exception. Why is this happening instead of an error stored within an Either data structure?
Based on information that I can find from here, here, and general other googling around… the instance for the Either monad does not have a
failimplementation. On a guess, that would be why I get an exception instead of Left. I wrote this little test to demonstrate:On the other hand,
fail :: String -> Maybe Stringreally does return Nothing. It would appear that the correct way to do my docToUser conversion is something more akin to this:I would imagine that can take quite a bit of refinement, especially in detecting and reporting which fields failed. But, this seems to be pretty close to the answer.
I think, given that, that this question is a duplicate of Is there no standard (Either a) monad instance?