Haskell: http-streams and Aeson
joshrotenberg.com
joshrotenberg.com
If you've been considering trying Haskell, you should go for it. Perhaps even start with a REST API project like this. You can learn the basics from "Learn You a Haskell"[1], which is a great book available free online, and you can install Haskell with the Haskell Platform[2], which has gotten a new release fairly recently.
I've been teaching my non-programmer friend Haskell and he's learning very quickly and enjoying it a great deal. He doesn't get frustrated because he has no assumptions about how things "should work".
Here's some code that extracts json from a web page (I hope to be generating haskell modules from API docs eventually.)
https://github.com/perurbis/hfreeagent/blob/master/fa-gen-ty...
chunks <- Streams.toList i
let feed = decode (BL.fromChunks chunks) :: Maybe Feed
You lose streaming here; this reads the entire response body into memory before sending it to Aeson to decode. Which is a shame since both Aeson and http-steams otherwise seem able to stream arbitrarily large content through without buffering lots of it in memory. (I have not tested how well Aeson manages this; just looking at the types!) This could be used to eg, fold an operation over the list of earthquakes, no matter how long the list becomes.Fixing this is probably beyond the scope of a simple walkthrough. I suspect it could be managed using unsafeInterleaveIO to translate between the io-streams chunks and the lazy bytestring chunks. Might be a nice thing to ask the io-streams developers to add. (Dealing with resource finalization could make it tricky though.)
On a less esoteric note, fetchQuakes would be improved by using withConnection. This would make it shorter, and ensures the stream is closed if there's an error.
import Control.Applicative
import Data.Aeson
import Data.ByteString
import System.IO.Streams
import System.IO.Streams.Attoparsec (parseFromStream)
parseJSONFromStream :: FromJSON a => InputStream ByteString -> IO a
parseJSONFromStream = parseJSON <$> parseFromStream json'receiveResponse con (\_ i -> parseFromStream json i))
This seems to work but without the benefit of being marshalled into my type.
receiveResponse con (fmap parseJSON . parseFromStream json) data MetaData = MetaData {
metadataUrl :: String,
metadataTitle :: String ...
you can use import Data.Char
...
upStr n s = toLower (head (drop n s)) : (drop (n+1) s)
$(deriveJSON (upStr 8) ''MetaData)
This will splice the `metadataUrl` to use the `url` field from JSON. The deriveJSON function takes a function to change the field names.It's pretty handy.
http://joshrotenberg.com/haskell/2013/06/05/follow-up-http-s...