Swizec Teller - a geek with a hatswizec.com

    Writing a REST client in Haskell

    A few days ago I decided to buy some bitcoin. Then I noticed it fluctuates a lot despite a general upwards trend. Hmmm ... if I just bought at the right moment and sold at a different right moment, I could make money fall out of the void!

    I have since lost $5 by playing and gained $30 by leaving it alone. Obviously I suck at this ...

    I know! Let's make a bot that does this! A low frequency trading bot, that sounds like fun. And let's write it in Haskell, just to keep things interesting. Marrying the strictest of languages with the messiest of resources - the internet ... what could possibly go wrong?

    First order of business - a REST client.

    REST client

    Before my bot can do any trading and intricate algorithmic trading, it needs to talk to the marketplace of choice. I picked Bitstamp because they're the only ones that let me do this without a US bank account.

    Writing a REST client in most languages is simple. Reading Bitstamp's ticker in Python looks like this:

    import requests, json
    r = requests.get("https://www.bitstamp.net/api/ticker")
    print json.loads(r.content)
    # prints: {u'volume': u'17179.28558844', u'last': u'159.49', u'bid': u'159.49', u'high': u'161.00', u'low': u'139.00', u'ask': u'159.64'}

    That's it. Everything you need for a set of values nicely accessible as a dictionary.

    In Haskell, well in Haskell figuring out how to do that took me all night, then a bit of the morning and finally a helpful tweet from a stranger to tell me just how I was misusing monads.

    First of all, we are going to need a bunch of imports. The ones we really care about are the http-conduit library and the Aeson parser of JSON strings. Everything else is there because ... well I'm not sure actually, but it seems to be necessary, otherwise things don't work.

    {-# LANGUAGE OverloadedStrings #-}
    import Network.HTTP.Conduit
    import Control.Monad.IO.Class
    import Data.ByteString.Lazy
    import Data.Aeson
    import Data.Attoparsec.Number
    import Control.Applicative
    import Control.Monad.Trans

    I am not perfectly certain what the OverloadedStrings bit at the top does. It's some sort of compiler directive and most haskell libraries I find in the wild tell me I will be a much happier man if I turn it on. Shrug.

    All it takes now is making an HTTP request and parsing the response as JSON. Simple, right?

    Well, Haskell is strict and you can't just parse things all willy nilly. We need to tell the parser what we expect, what we want to do with the result and what type it's going to be. Can't just have a bag of stuff! Nope, needs to be a well defined bag of things.

    data Ticker = Ticker
    { high :: Number,
    last :: Number,
    bid :: Number,
    volume :: Number,
    low :: Number,
    ask :: Number
    } deriving Show

    Great. We have a Ticker type that has a bunch of numbers and some names. That _Show _part seems to say that we'll be able to print this out to the console. Smashing!

    That's not enough though, it is time for some strange hieroglyphics that tell Aeson how exactly parsing works.

    instance FromJSON Ticker where
    parseJSON (Object v) = Ticker
    v .: "high"
    v .: "last"
    v .: "bid"
    v .: "volume"
    v .: "low"
    v .: "ask"

    If I understand this correctly, those strange symbols are applicatives. The .: does ... something ... and the <$> _and <*> do something else. The whole bit is about defining how to convert a key in the JSON string into a value in the Ticker type. I think.

    Right, let's make a function that will talk to the server and return a Ticker object. Maybe. If all goes well.

    ticker::(MonadIO m) => m (Maybe Ticker)
    ticker = get "ticker" >>= return . decode

    Fairly simple stuff. Take something from the internet carefully wrapped in MonadIO, unwrap it for a bit, feed it into decode, which magically uses all the stuff we defined earlier, and wrap it back into both a MonadIO and a Maybe. Parsing can fail you know.

    get::(MonadIO m) => String -> m ByteString
    get url = simpleHttp $ "https://www.bitstamp.net/api/"++url

    This is the generalized get function that talks to Bitstamp using the simpleHttp function from http-conduit. It looks simple, but I'm sure a lot of hairy stuff is going on behind the scenes.

    To make sure everything works, we run it.

    main = do
    ticker >>= print


    Yup, the output we get is Nothing. It is at this point you realize someone isn't using JSON correctly and all those numbers are actually strings. Strings. Now how the hell do you tell Haskell to automagically transform those into Numbers before putting them in the Ticker object?

    Messy messy internet.

    But hey! Got Haskell to talk to a REST API. How cool is that!?

    Enhanced by Zemanta

    Did you enjoy this article?

    Published on April 25th, 2013 in Aeson, Haskell, JSON, Languages, Monads, Parsing, Uncategorized,

    Learned something new?
    Read more Software Engineering Lessons from Production

    I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.

    Subscribe below 👇

    Software Engineering Lessons from Production

    Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"

    ~ Ashish Kumar

    Join 15,883+ engineers learning lessons from my "raw and honest from the heart" emails.

    4.5 stars average rating

    Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.

    Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.

    Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.

    Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev

    Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization

    Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections

    Want to brush up on modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created by Swizec with ❤️