4

I'm trying to do JSON parsing in IO:

{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import Data.Aeson
import Data.Maybe (fromJust)

main :: IO ()
main = do
  response <- getResponseBody <$> httpJSON "http://localhost:9200" :: IO Object
  name <- fromJust <$> response .: "name" :: Parser String
  print "hi"

I get the error:

/home/nut/dev/haskell/elastic/app/Main.hs:39:11: error:
    • Couldn't match type ‘Parser’ with ‘IO’
      Expected type: IO String
        Actual type: Parser String
    • In a stmt of a 'do' block:

So how do I get that name out of the json result?

Cactus
  • 27,075
  • 9
  • 69
  • 149
McBear Holden
  • 5,741
  • 7
  • 33
  • 55
  • 2
    It looks like you’re trying to bind the result of `fromJust <$> response .: "name"` in `IO`, but it’s just a `Parser` value. I’m not very familiar with Aeson, but I think you need to run the parser (purely) with `Data.Aeson.Types.parse` or `parseMaybe`. – Jon Purdy Nov 22 '16 at 22:43

1 Answers1

6

Aeson has a bunch of functions to go from Parser a to a:

parse       :: (a -> Parser b) -> a -> Result b
parseEither :: (a -> Parser b) -> a -> Either String b
parseMaybe  :: (a -> Parser b) -> a -> Maybe b

so if you have e.g.

(.: "name") :: Object -> Parser String

then you have

parseMaybe (.: "name") :: Object -> Maybe String

so you can do

{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import Data.Aeson
import Data.Maybe (fromJust)
import Data.Aeson.Types -- new import for parseMaybe

main :: IO ()
main = do
    response <- getResponseBody <$> httpJSON "http://localhost:9200"
    let name = fromJust $ parseMaybe (.: "name") response :: String
    print "hi"
Cactus
  • 27,075
  • 9
  • 69
  • 149
  • Now I'm a little confused. wouldn't it be `(.: "name") :: Value -> Parser String` ? The type of `Object` is `Value`? Sorry for being confused about the Aeson library... – LudvigH Jul 11 '17 at 14:30
  • Yes, that is correct. The whole expression `fromJust $ parseMaybe (.: "name") response` is what we annotate as having type `String`: `(.: "name") :: Value -> Parser String`, and `response :: Value`, so we can apply `parseMaybe :: (a -> Parser b) -> a -> Maybe b` with `a ~ Value` and `b ~ String`. – Cactus Jul 12 '17 at 03:37
  • 1
    Of course, if we just used `name` for anything at all (like changing the last line from `print "hi"` to `putStrLn $ unwords ["Hi,", name]`) that prescribes its type, then we don't need the type annotation in the `let name = ...` line. – Cactus Jul 12 '17 at 03:38
  • I see. I'm new to polymorphism, so the explanation helps. :) thanks a lot! – LudvigH Jul 12 '17 at 05:21