JSON, Aeson and Template Haskell for fun and profit

At times, handling JSON in Haskell might seem difficult, but once you get the hang of it, you will in fact see it is not.

There are a few libraries that parse and encode JSON in Haskell, but recently one particular has gained a lot of popularity, and for a good reason. The library in question is Bryan O’Sullivan’s Aeson.

Aeson performs better than its older relative JSON, all while simplifying the implementation of encoding/decoding functions by using Template Haskell.

Since all this imposing knowledge came out of an effort to build a Meetup.com API client library in Haskell, we’ll be dealing with Meetup entities in the following examples. Consequently, we have an Eventtype that was obtained by querying the /2/events API endpoint (example response), and an RSVPtype, which we get from the /2/rsvpsAPI endpoint (example response).

Hard labour

The regular way to parse JSON with Aeson is to implement a FromJSONinstance for the data type we want to build from JSON. It tells Aeson what JSON fields to pluck from and how to translate those fields to a Haskell construct. Inversely, if we wanted to encode a type to JSON, we’d have to implement a ToJSONinstance.

Here’s an example of how to manually implement a FromJSONinstance for an Eventtype.

Manual JSON parsing

If you are interested, you can load the file into the GHCi REPL with $ ghci src/Types/Event.hsand see what the sampleEventspits out.

As you can see, following the Eventtype definition, there’s a definition of the parseJSONfunction, and it looks like the parseJSONdefinition could be categorised as a boilerplate code. Why? Because each time we add or remove a record field in the Eventdata constructor, we also need to change the parseJSONdefinition accordingly.

There is more. If the example above contained a ToJSONdefinition, we’d have to change it as well. Now, stretch your imagination for a second and imagine we had more than one type. This situation could get out of hand very quickly and even though the type system can inform us of it, it would still be annoying.

Making the GHC work for you

Feeling tired after all the hard work we had to do previously, it kind of makes us wonder if there’s an easier way to do this… Turns out there is, and it’s called Template Haskell (called TH by friends).

I won’t go into the whats and the hows of TH (mainly since I myself don’t completely understand it yet), but you can think of it as Lisp’s macro system but with types. Because everything is better with some types.

Long story short, using the Data.Aeson.TH package we can make the GHC define the FromJSON and ToJSON instances at compile-time for us, and this is how:

Leveraging template Haskell

As before, you can load up the code into GHCi the same as before and check the result by calling sampleRSVP.

We can see in the example above that, even though there is no fromJSON definition, we’re still able to decode the ByteString to JSON. We still need to import Data.Aeson to have access to the decode function.

Not much convincing is needed to see that this approach is much better than the previous one. Once Event or RSVP types change, GHC changes the `FromJSON` and `ToJSON` definitions at our demand.

Conclusion

Reduce boilerplate wherever possible, since the tools to do it are already there. You’ll be happier and your general well-being will improve.

In some future post I’ll try to figure out Template Haskell and explain it in an approachable way.

P.S.

If anyone can explain what “A continuation-based parser type” means, I’d be happy to hear it out.


Originally published at pltconfusion.com.