Json parser, incorrectly parsing string as a number - json

I'm still pretty new to Haskell and functional programming in general, so I'm writing a small program with Parsec to parse JSON and pretty print it as a means of learning basic concepts. This is what I have so far:
import Text.Parsec
import Text.Parsec.String
data JValue = JString String
| JNumber Double
| JBool Bool
| JNull
| JObject [(String, JValue)]
| JArray [JValue]
deriving (Eq, Ord, Show)
parseJString, parseJNumber, parseJBool, parseJNull :: Parser JValue
parseJString = do
str <- between (char '"') (char '"') (many (noneOf "\""))
return . JString $ str
parseJNumber = do
num <- many digit
return . JNumber . read $ num
parseJBool = do
val <- string "true" <|> string "false"
case val of
"true" -> return (JBool True)
"false" -> return (JBool False)
parseJNull = string "null" >> return JNull
parseJValue :: Parser JValue
parseJValue = parseJString
<|> parseJNumber
<|> parseJBool
<|> parseJNull
For now, I'm assuming that the numbers are integers. Individually, parseJString, parseJNumber, parseJBool, and parseJNull work as expected in ghci. Additionally, parseJValue correctly parses strings and numbers.
ghci> parse parseJString "test" "\"test input\""
Right (JString "test input")
ghci> parse parseJNumber "test" "345"
Right (JNumber 345.0)
ghci> parse parseJBool "test" "true"
Right (JBool True)
ghci> parse parseJNull "test" "null"
Right JNull
ghci> parse parseJValue "test" "\"jvalue test\""
Right (JString "jvalue test")
ghci> parse parseJValue "test" "789"
Right (JNumber 789.0)
parseJValue fails, however, when I try to parse true, false, or null, and I get an interesting error.
ghci> parse parseJValue "test" "true"
Right (JNumber *** Exception: Prelude.read: no parse
I get a successful parse, but the parse returns a JNumber followed by an error stating that Prelude.read failed. I feel like I'm missing some core concept in building my parsers, but I can't see where I've gone wrong. Also, am I making any beginner mistakes with my code, i.e. would any of this be considered "bad" haskell?

The problem is the usage of many in parseJNumber. It is also a valid parse, when no character of the following string is consumed ("many p applies the parser p zero or more times. [...]"). What you need is many1:
parseJNumber = do
num <- many1 (oneOf "0123456789")
return $ JNumber (read num :: Double)
Edit:
Somehow, I think your combination of (.) and ($) looks kind of weird. I use (.) when I can get rid of a function parameter (like in the usage of (>>=)) and ($) when I'm to lazy to write parentheses. In your function parseJString you do not need (.) in order to get the right binding precedences. (I did the same transformation in the code above.)
parseJString = do
str <- between (char '"') (char '"') (many (noneOf "\""))
return $ JString str
Additionally, you could eliminate code-repetition by refactoring parseJBool:
parseJBool = do
val <- string "true" <|> string "false"
return (case val of
"true" -> JBool True
"false" -> JBool False)
I would even rewrite the case-construct into a (total) local function:
parseJBool = (string "true" <|> string "false") >>= return . toJBool
where
-- there are only two possible strings to pattern match
toJBool "true" = JBool True
toJBool _ = JBool False
Last but not least, you can easily transform your other functions to use (>>=) instead of do-blocks.
-- additionally, you do not need an extra type signature for `read`
-- the constructor `JNumber` already infers the correct type
parseJNumber =
many1 (oneOf "0123456789") >>= return . JNumber . read
parseJString =
between (char '"') (char '"') (many (noneOf "\"")) >>= return . JString

You should try with many1 digit rather than many digit. A many succeeds on zero occurrences of the argument.
Compare:
ghci> parse (many digit) "test" "true"
Right ""
ghci> parse (many1 digit) "test" "true"
unexpected "t"
expecting digit
So in your case, parseJNumber within parseJValue will succeed and return an empty string which is then passed to read. But read "" :: Double fails.

Related

Parse strings that are surrounded with quotes

I'm parsing some data I don't control. I have values that are an array of strings. They can either be normal strings, a string representation of a number, or a number with quotes around it.
["This is just a string", "\"5\"", "3"]
I would like to write a function toValue that converts them into the appropriate type to be converted to JSON.
toValue :: (ToJSON a) => String -> a
toValue (if a number) = parseInt
toValue (if a quoted number) = parseInt . stripQuotes
toValue _ = id
I would like to strip the quotes if it is a number surrounded by quotes, then convert it to a number if a number, otherwise pass it back as a string.
Can I do this with pattern matching? Some other way?
import Data.Char
import Data.Bool
parse a#('"':n:'"':[]) = bool (Left a) (Right (read [n] :: Int)) (isNumber n)
parse a#('"':n:m:'"':[]) = bool (Left a) (Right (read [n,n] :: Int)) (isNumber n && isNumber m)
parse a#(n:[]) = bool (Left a) (Right (read [n] :: Int)) (isNumber n)
parse a#(n:m:[]) = bool (Left a) (Right (read [n,n] :: Int)) (isNumber n && isNumber m)
parse xs = Left xs
> map parse ["This is just a string", "\"5\"", "3"]
[Left "This is just a string",Right 5,Right 3]
then, you can use either function from Data.Either module to encode number (Rights) and string (Lefts) to JSON.
Writing a function toValue :: ToJSON a => String -> a as you are proposing is not so hard: let us simply make toValue a method of the class ToJSON
class ToJSON a where
toValue :: String -> a
and then define the instance for Int as
instance ToJSON Int where
toValue s#('\"' : _) = read (read s) -- with quotes
toValue s = read s -- without quotes
The instance for String is slightly more involved (as String is a synonym for [Char]) but by no means rocket science:
class ToJSONList a where
toValues :: String -> [a]
instance ToJSONList Char where
toValues = id
instance ToJSONList a => ToJSON [a] where
toValue = toValues
Now, testing it in an interactive session, we have:
> toValue "This is just a string" :: String
"This is just a string"
> toValue "\"5\"" :: Int
5
> toValue "3" :: Int
3
However, from your question it seems that you have a use case that is not well supported by such a function toValue, i.e., to convert all elements of a list to their appropriate JSON-representation. To do so, you probably want to introduce an algebraic datatype for representing JSON-values:
data JSON = JInt Int | JString String deriving Show
and then have function toJSON that takes strings to their most appropriate JSON-representation:
toJSON :: String -> JSON
toJSON s =
case reads s of
[(n, "")] -> JInt n -- Int, no quotes
_ -> case reads s of
[(s, "")] -> case reads s of
[(n, "")] -> JInt n -- Int, quotes
_ -> JString s -- String, quotes
_ -> JString s -- String, no quotes
Indeed, to answer that part of your question, this function is defined just by (nested) pattern matching. However, if the (grammar of the) language you need to parse becomes more complicated then just integer literals, quoted integer literals, and string literals, this way of defining parsers quickly becomes to clumsy and error-prone, and you may want to start looking into parser combinators or parser generators then.
For your simple language of JSON values, this is arguably still fine though. Here is a simple example in an interactive session:
> map toJSON ["This is just a string", "\"5\"", "3"]
[JString "This is just a string",JInt 5,JInt 3]

Defining and Catching Exceptions

I'm still getting the hang of haskell, and am trying to build my first "real" coding project, i.e. one where I'm going to including testing, write up documentation, etc. I'm sufficiently new to haskell where I know I don't have a lot of the knowledge of the language necessary so that everything I want to do is immediately within reach, but that's kind of the idea, so that the act of finishing will require that I touch most of the major pieces of the language.
Anyway, so the current issue I'm having is regarding throwing and catching exceptions in the language, something that I understand can be done with quite varied approaches. I have a function here, toLower:
toLower :: String -> String
toLower plaintext =
if (catch (nonAlpha plaintext) handlerNonAlpha)
then map charToLower plaintext
else exitFailure
Which will take a string, throw an exception and exit if the string includes any non-alpha characters (so if not A-Z or a-z), or if not convert the string to lowercase. So what I have for the nonAlpha function is:
--- detect non-alpha character - throw error if existant
data NonNumericException = NonNumException
instance Exception NonNumericException
handlerNonAlpha :: NonNumericException -> IO()
handlerNonAlpha ex =
putStrLn "Caught Exception: " ++ (show ex) ++ " - A non-alpha character was included in the plaintext."
nonAlpha :: String -> Bool
nonAlpha str =
let nonalphas = [x | x <- str, (ord x) < 65 || (90 < (ord x) && (ord x) < 97) || 123 < (ord x)]
in if (length nonalphas) == 0
then True
else throw NonNumException
As I said I'm pretty new to haskell, so I'm a little vague on how this data/instance structure works, but as I understand it I'm defining an parent NonNumericException, of which NonNumException is a child (and I could have more), and in the instance line defining them to be Exceptions. The catch structure, if it detects an exception (for instance, when one is thrown at the end of nonAlpha if there is a non-alpha character), then calls the handler.
So here are the compile errors that I get:
utilities.hs:61:3:
Couldn't match expected type `[Char]' with actual type `IO ()'
In the return type of a call of `putStrLn'
In the first argument of `(++)', namely
`putStrLn "Caught Exception: "'
In the expression:
putStrLn "Caught Exception: "
++
(show ex)
++ " - A non-alpha character was included in the plaintext."
utilities.hs:61:3:
Couldn't match expected type `IO ()' with actual type `[Char]'
In the expression:
putStrLn "Caught Exception: "
++
(show ex)
++ " - A non-alpha character was included in the plaintext."
In an equation for `handlerNonAlpha':
handlerNonAlpha ex
= putStrLn "Caught Exception: "
++
(show ex)
++ " - A non-alpha character was included in the plaintext."
utilities.hs:73:7:
Couldn't match expected type `Bool' with actual type `IO ()'
In the return type of a call of `catch'
In the expression: (catch (nonAlpha plaintext) handlerNonAlpha)
In the expression:
if (catch (nonAlpha plaintext) handlerNonAlpha) then
map charToLower plaintext
else
exitFailure
utilities.hs:73:14:
Couldn't match expected type `IO ()' with actual type `Bool'
In the return type of a call of `nonAlpha'
In the first argument of `catch', namely `(nonAlpha plaintext)'
In the expression: (catch (nonAlpha plaintext) handlerNonAlpha)
utilities.hs:75:8:
Couldn't match type `IO a0' with `[Char]'
Expected type: String
Actual type: IO a0
In the expression: exitFailure
In the expression:
if (catch (nonAlpha plaintext) handlerNonAlpha) then
map charToLower plaintext
else
exitFailure
In an equation for `toLower':
toLower plaintext
= if (catch (nonAlpha plaintext) handlerNonAlpha) then
map charToLower plaintext
else
exitFailure
So I guess my two question are, a) what's going wrong with the types for the handler (the line 61 errors), and b) how do I properly set the types for the functions that may throw an exception or exit with failure, but otherwise will return a bool or a string?
EDIT: I guess I should note. I do see the similarities between this question and a number of others that have been asked. Part of what I'm looking for that I don't see is a description of what the structures here are actually doing, and what is best practice and why.
What is best practice in Haskell is to leverage the awesome power of its type system to avoid needing to throw/catch exceptions for pure functions. There are cases where throwing an exception can actually make sense, but for something like your toLower function you can just choose to have a different return type. For example:
-- We can factor out our check for a non-alpha character
isNonAlpha :: Char -> Bool
isNonAlpha c = c' < 65 || (90 < c' && c' < 97) || 123 < c'
where c' = ord c
-- Why throw an exception? Just return False
hasNonAlpha :: String -> Bool
hasNonAlpha str = any isNonAlpha str
-- Renamed to not conflict with Data.Char.toLower
myToLower :: String -> Maybe String
myToLower plaintext =
if hasNonAlpha plaintext
then Nothing
else Just $ map toLower plaintext
Not only is this cleaner code, but now we don't have to worry about error handling at all, and someone else using your code won't get a nasty surprise. Instead, the notion of failure is encoded at the type level. To use this as an "error handling" mechanism, just work in the Maybe monad:
doSomething :: String -> String -> Maybe String
doSomething s1 s2 = do
s1Lower <- myToLower s1
s2Lower <- myToLower s2
return $ s1Lower ++ s2Lower
If either myToLower s1 or myToLower s2 returns Nothing, then doSomething will return Nothing. There is no ambiguity, no chance for an unhandled exception, and no crashing at runtime. Haskell exceptions themselves, those thrown by the function throw, must be caught by catch, which has to execute in the IO monad. Without the IO monad, you can't catch exceptions. In pure functions, you can always represent the concept of failure with another data type without having to resort to throw, so there is no need to over-complicate code with it.
Alternative, you could have even written myToLower monadically as
import Control.Monad
-- Other code
myToLower :: String -> Maybe String
myToLower plaintext = do
guard $ not $ hasNonAlpha plaintext
return $ map toLower plaintext
The guard from Control.Monad acts as a sort of filter for MonadPlus instances. Since Maybe is an instance of MonadPlus (as are lists), this gives us very simple code.
Or, if you want to pass around an error message:
type MyError = String
myToLower :: String -> Either MyError String
myToLower plaintext = if hasNonAlpha plaintext
then Left $ "The string " ++ plaintext ++ " has non-alpha character(s)"
else Right $ map toLower plaintext
Then you can change the type of doSomething to match:
doSomething :: String -> String -> Either MyError String
doSomething s1 s2 = do
s1Lower <- myToLower s1
s2Lower <- myToLower s2
return $ s1Lower ++ s2Lower
If you notice, the monadic syntax lets us change the type signature of our function without even having to change the code! Play around with this implementation to get a feel for how it works.
Learning about exceptions is useful, and they are great for handling exceptional circumstances.
The best place to read about exceptions is Simon Marlow's paper, An Extensible Dynamically-Typed Heirarchy of Exceptions. His book, Parallel Concurrent Programming in Haskell is another good resource on their use.
The following are a few comments on your question.
error on line 61
handlerNonAlpha :: NonNumericException -> IO()
handlerNonAlpha ex =
putStrLn "Caught Exception: " ++ (show ex) ++ ...
Function arguments are consumed eagerly in haskell. You'll have to modify this line as follows to perform string concatenation before calling putStrLn:
putStrLn $ "Caught Exception: " ++ (show ex) ++ ...
comment on nonAlpha
Exceptions can only be caught from an IO computation, and it's best to avoid throwing them from pure functions. Besides this, the problem with nonAlpha is that it claims to return a Bool, but actually returns either True or throws an exception. Why not just return False?
nonAlpha :: String -> Bool
nonAlpha str =
let nonalphas = [x | x <- str, (ord x) < 65 || (90 < (ord x) && (ord x) < 97) || 123 < (ord x)]
in if (length nonalphas) == 0
then True
else False
Pull your exception throwing code out of nonAlpha like so. The name of this function and its lack of return value indicate that it might throw an exception:
trapInvalid :: String -> IO ()
trapInvalid str = unless (nonAlpha str) $ throw NonNumException

How do I print the name and value of a custom data type in Haskell

Lets say I define a data type as follows:
data OP = Plus | Minus | Num Int deriving (Show, Eq)
Then I take a list of strings, and get a list of their respective OP values like this:
getOp :: [String] -> [OP]
getOp [] = []
getOp (x:rest)
| x == "+" = Plus:(getOp rest)
| isInfixOf "Num" x == True = Num (read (drop 4 x) :: Int):(getOp rest)
| otherwise = "-" = Minus:(getOp rest)
I then want to show the [OP] list, separated by new lines. I've done it with list of Strings easily, but not sure what to do with a list of data types.
I have the following structure as a starting point:
showOp :: [OP] -> String
showOp [] = []
showOp (o:os) = (putStr o):'\n':(showOp os)
I know the last line is wrong. I'm trying to return a [Char] in the first section, then a Char, then a recursive call. I tried some other variations for the last line (see below) with no luck.
showOp o = show o (works but not what I need. It shows the whole list, not each element on a new line
showOp o = putStrLn (show o) (epic fail)
showOp o
| o == "+" = "Plus\n":(showOp os)
| more of the same. Trying to return a [Char] instead of a Char, plus other issues.
Also, i'm not sure how the output will need to be different for the Num Int type, since I'll need to show the type name and the value.
An example i/o for this would be something like:
in:
getOp ["7","+","4","-","10"]
out:
Num 7
Plus
Num 4
Minus
Num 10
You need to look at the types of the functions and objects you are using. Hoogle is a great resource for getting function signatures.
For starters, the signature of putStr is
putStr :: String -> IO ()
but your code has putStr o, where o is not a string, and the result should not be an IO (). Do you really want showOp to print the Op, or just make a multi-line string for it?
If the former, you need the signature of showOp to reflect that:
showOp :: [Op] -> IO ()
Then you can use some do-notation to finish the function.
I'll write a solution for your given type signature. Since showOp should return a String and putStr returns an IO (), we won't be using putStr anywhere. Note that String is simply a type synonym for [Char], which is why we can treat Strings as a list.
showOp :: [Op] -> String
showOp [] = [] -- the empty list is a String
showOp (o:os) = showo ++ ('\n' : showos)
where showo = (show o) -- this is a String, i.e. [Char]
showos = showOp os -- this is also a String
Both showo and showos are Strings: both show and showOp return Strings.
We can add a single character to a list of characters using the cons operation :. We can append two lists of strings using append operator ++.
Now you might want another function
printOp :: [Op] -> IO ()
printOp xs = putStr $ showOp xs
How about:
showOp = putStrLn . unlines . map show
Note that your data constructor OP is already an instance of Show. Hence, you can actually map show into your array which contains members of type OP. After that, things become very somple.
A quick couple of notes ...
You might have wanted:
getOp :: [String] -> [OP]
getOp [] = []
getOp (x:rest)
| x == "+" = Plus:(getOp rest)
| x == "-" = Minus:(getOp rest)
| isInfixOf "Num" x == True = Num (read (drop 4 x) :: Int):(getOp rest)
| otherwise = (getOp rest)
Instead of what you have. Your program has a syntax error ...
Next, the input that you wanted to provide was probably
["Num 7","+","Num 4","-","Num 10"]
?. I guess that was a typo.

json parsing in haskell part 2 - Non-exhaustive patterns in lambda

This is actually in continuation of the question I asked a few days back. I took the applicative functors route and made my own instances.
I need to parse a huge number of json statements all in a file, one line after the other. An example json statement is something like this -
{"question_text": "How can NBC defend tape delaying the Olympics when everyone has
Twitter?", "context_topic": {"followers": 21, "name": "NBC Coverage of the London
Olympics (July & August 2012)"}, "topics": [{"followers": 2705,
"name": "NBC"},{"followers": 21, "name": "NBC Coverage of the London
Olympics (July & August 2012)"},
{"followers": 17828, "name": "Olympic Games"},
{"followers": 11955, "name": "2012 Summer Olympics in London"}],
"question_key": "AAEAABORnPCiXO94q0oSDqfCuMJ2jh0ThsH2dHy4ATgigZ5J",
"__ans__": true, "anonymous": false}
sorry for the json formatting. It got bad
I have about 10000 such json statements and I need to parse them. The code I have written is
something like this -
parseToRecord :: B.ByteString -> Question
parseToRecord bstr = (\(Ok x) -> x) decodedObj where decodedObj = decode (B.unpack bstr) :: Result Question
main :: IO()
main = do
-- my first line in the file tells how many json statements
-- are there followed by a lot of other irrelevant info...
ts <- B.getContents >>= return . fst . fromJust . B.readInteger . head . B.lines
json_text <- B.getContents >>= return . tail . B.lines
let training_data = take (fromIntegral ts) json_text
let questions = map parseToRecord training_data
print $ questions !! 8922
This code gives me a runtime error Non-exhaustive patterns in lambda. The error references to \(Ok x) -> x in the code. By hit and trial, I came to the conclusion that the program works ok till the 8921th index and fails on the 8922th iteration.
I checked the corresponding json statement and tried to parse it standalone by calling the function on it and it works. However, it doesn't work when I call map. I don't really understand what is going on. Having learnt a little bit of haskell in "learn haskell for a great good", I wanted to dive into a real world programming project but seem to have got stuck here.
EDIT :: complete code is as follows
{-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -O2 -optc-O2 #-}
{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Maybe
import NLP.Tokenize
import Control.Applicative
import Control.Monad
import Text.JSON
data Topic = Topic
{ followers :: Integer,
name :: String
} deriving (Show)
data Question = Question
{ question_text :: String,
context_topic :: Topic,
topics :: [Topic],
question_key :: String,
__ans__ :: Bool,
anonymous :: Bool
} deriving (Show)
(!) :: (JSON a) => JSObject JSValue -> String -> Result a
(!) = flip valFromObj
instance JSON Topic where
-- Keep the compiler quiet
showJSON = undefined
readJSON (JSObject obj) =
Topic <$>
obj ! "followers" <*>
obj ! "name"
readJSON _ = mzero
instance JSON Question where
-- Keep the compiler quiet
showJSON = undefined
readJSON (JSObject obj) =
Question <$>
obj ! "question_text" <*>
obj ! "context_topic" <*>
obj ! "topics" <*>
obj ! "question_key" <*>
obj ! "__ans__" <*>
obj ! "anonymous"
readJSON _ = mzero
isAnswered (Question _ _ _ _ status _) = status
isAnonymous (Question _ _ _ _ _ status) = status
parseToRecord :: B.ByteString -> Question
parseToRecord bstr = handle decodedObj
where handle (Ok k) = k
handle (Error e) = error (e ++ "\n" ++ show bstr)
decodedObj = decode (B.unpack bstr) :: Result Question
--parseToRecord bstr = (\(Ok x) -> x) decodedObj where decodedObj = decode (B.unpack bstr) :: Result Question
main :: IO()
main = do
ts <- B.getContents >>= return . fst . fromJust . B.readInteger . head . B.lines
json_text <- B.getContents >>= return . tail . B.lines
let training_data = take (fromIntegral ts) json_text
let questions = map parseToRecord training_data
let correlation = foldr (\x acc -> if (isAnonymous x == isAnswered x) then (fst acc + 1, snd acc + 1) else (fst acc, snd acc + 1)) (0,0) questions
print $ fst correlation
here's the data which can be given as input to the executable. I'm using ghc 7.6.3. If the program name is ans.hs, I followed these steps.
$ ghc --make ans.hs
$ ./ans < path/to/the/file/sample/answered_data_10k.in
thanks a lot!
The lambda function (\(Ok x) -> x) is partial in that it will only be able to match objects that were successfully decoded. If you are experiencing this, it indicates that your JSON parser is failing to parse a record, for some reason.
Making the parseToRecord function more informative would help you find the error. Try actually reporting the error, rather than reporting a failed pattern match.
parseToRecord :: B.ByteString -> Question
parseToRecord bstr = handle decodedObj
where handle (Ok k) = k
handle (Error e) = error e
decodedObj = decode (B.unpack bstr) :: Result Question
If you want more help, it might be useful to include the parser code.
Update
Based on your code and sample JSON, it looks like your code is first failing
when it encounters a null in the context_topic field of your JSON.
Your current code cannot handle a null, so it fails to parse. My fix would
be something like the following, but you could come up with other ways to
handle it.
data Nullable a = Null
| Full a
deriving (Show)
instance JSON a => JSON (Nullable a) where
showJSON Null = JSNull
showJSON (Full a) = showJSON a
readJSON JSNull = Ok Null
readJSON c = Full `fmap` readJSON c
data Question = Question
{ question_text :: String,
context_topic :: Nullable Topic,
topics :: [Topic],
question_key :: String,
__ans__ :: Bool,
anonymous :: Bool
} deriving (Show)
It also seems to fail on line 9002, where there is a naked value of "1000" on
that line, and it seems that several JSON values after that line lack the
'__ans__' field.
I would have suggestion to use Maybe in order to parse the null values:
data Question = Question
{ question_text :: String
, context_topic :: Maybe Topic
, topics :: [Topic]
, question_key :: String
, __ans__ :: Bool
, anonymous :: Bool
} deriving (Show)
And then change the readJSON function as follows (in addition, the missing ans-fields can be fixed by returning False on an unsuccessful parsing attempt):
instance JSON Question where
-- Keep the compiler quiet
showJSON = undefined
readJSON (JSObject obj) = Question <$>
obj ! "question_text" <*>
(fmap Just (obj ! "context_topic") <|> return Nothing) <*>
obj ! "topics" <*>
obj ! "question_key" <*>
(obj ! "__ans__" <|> return False) <*>
obj ! "anonymous"
readJSON _ = mzero
After getting rid of the 1000 in line 9000-something (like sabauma mentioned), I got 4358 as result. So maybe these slight changes are enough?

Why pattern matching does not throw exception in Maybe monad

My question is simple. Why wrong pattern matching does not throw exception in Maybe monad. For clarity :
data Task = HTTPTask {
getParams :: [B.ByteString],
postParams :: [B.ByteString],
rawPostData :: B.ByteString
} deriving (Show)
tryConstuctHTTPTask :: B.ByteString -> Maybe Task
tryConstuctHTTPTask str = do
case decode str of
Left _ -> fail ""
Right (Object trie) -> do
Object getP <- DT.lookup (pack "getParams") trie
Object postP <- DT.lookup (pack "postParams") trie
String rawData <- DT.lookup (pack "rawPostData") trie
return $ HTTPTask [] [] rawData
Look at tryConstuctHTTPTask function. I think that when the pattern does not match (for example "Object getP") we must get something like "Prelude.Exception", instead i get the "Nothing". I like this behavior but i am not understand why.
Thanks.
Doing pattern <- expression in a do-block, will call fail when the pattern does not match. So it is equivalent to doing
expression >>= \x ->
case x of
pattern -> ...
_ -> fail
Since fail is defined as Nothing in the Maybe monad, you get Nothing for failed pattern matches using <-.