Question

I'm a complete newbie to Haskell and having some trouble parsing JSON from a url. I have managed to code the parsing sides of things and have tested with Strings and they work fine. The trouble I'm getting is when I start working with IO.

I have the following code :

data Movie = Movie 
    { adult :: Bool, 
      backdrop_path :: Maybe String,
      id :: Int,
      original_title :: String,
      release_date :: String,
      poster_path :: Maybe String,
      popularity :: Int, 
      title :: String,
      vote_average :: Double,
      vote_count :: Int
    } deriving (Show)

data MovieList = MovieList {getMovies ::[Movie]} deriving (Show)

instance FromJSON Movie where
    parseJSON (Object v) = Movie <$>
                   (v .: "adult") <*>
                   (v .:? "backdrop_path") <*> -- Optional
                   (v .: "id") <*>
                   (v .: "original_title") <*>
                   (v .: "release_date") <*>
                   (v .:? "poster_path") <*> -- Optional
                   (v .: "popularity") <*>
                   (v .: "title") <*>
                   (v .: "vote_average") <*>
                   (v .: "vote_count")

instance FromJSON MovieList where
    parseJSON (Object o) = MovieList <$> o .: "results"
    parseJSON _          = mzero

movieAPIRequest :: String -> IO String
movieAPIRequest movieURI =
    do resp <- simpleHTTP request
       case resp of
         Left x -> return $ "Error connecting: " ++ show x
         Right r ->
             case rspCode r of
               (2,_,_) -> return $ rspBody r -- Request Fulfilled
               _ -> return $ show r -- Request Failed
    where request = Request {rqURI = uri, rqMethod = GET, rqHeaders = [], rqBody = ""}
          uri = fromJust $ parseURI movieURI

convertToByteString s = BS.pack s

main = do
    response <- movieAPIRequest url 
    decoded <- decode (convertToByteString response):: Maybe MovieList
    return $ decoded

I can't get the main to work. I want to automatically retrieve JSON from a url. The movieAPIRequest gives me the body of the request (JSON) as IO String. convertToByteString takes a String and converts to Data.ByteString.Lazy.Char8.ByteString as the decode function in Aeson takes a bytestring as an argument. With the above code I'm getting the following error:

[1 of 1] Compiling MovieDataType ( MovieDataType.hs, interpreted )

MovieDataType.hs:62:20:
    Couldn't match type `Maybe' with `IO'
    Expected type: IO MovieList
      Actual type: Maybe MovieList
    In a stmt of a 'do' block:
      decoded <- decode (convertToByteString response) :: Maybe MovieList
    In the expression:
      do { response <- movieAPIRequest url;
           decoded <- decode (convertToByteString response) ::
                        Maybe MovieList;
           return $ decoded }
    In an equation for `main':
        main
          = do { response <- movieAPIRequest url;
                 decoded <- decode (convertToByteString response) ::
                              Maybe MovieList;
                 return $ decoded }
Failed, modules loaded: none.

I've tried fixing it but keep getting different that I can't get my head around. For example this one is telling its expecting IO MovieList but decode should return a Maybe MovieList.

Was it helpful?

Solution

I see a couple of errors in your main function

main = do
    response <- movieAPIRequest url 
    decoded <- decode (convertToByteString response):: Maybe MovieList
    return $ decoded

First of all, if you look up "decode" at http://hackage.haskell.org/package/aeson-0.6.1.0/docs/Data-Aeson.html, you will see that it is of type

decode :: FromJSON a => ByteString -> Maybe a

Note that no IO is involved in this definition, so you can not extract any data from it using "<-". Instead, use "let decoded = ....".

You can read up on the theory behind this distinction as you learn more Haskell, but for now, just note that if a function returns type IO a, you extract data from it using "value <- function x y" (this is why the movieAPIRequest is fine), whereas if the function has no side effects, you extract the value using "let value = function x y". (it is more complicated than this, but this is the 90% answer that will keep you going until you learn about monads).

Second, you are returning a value from main, which is of type IO(). This is the hardcoded type of main in any Haskell program, so you can't return any value except "()". Instead, you have to do something with the value "decoded" (like print it) before the program ends.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top