JSON and Monads in Haskell
The problem is simple, but it took me some hours to figure it out. I had a JSON file composed of a single dictionary where the values were lists of numbers, discrete and continuous, and I wanted to keep the different number types while reading it in Haskell.
This was a challenge to me, since my practical Haskell experience had been around implementing some classical data structure algorithms.
Structuring the solution
From Artyom’s tutorial about Aeson, I had the feeling that my main problem would be to define a type to describe the numerical values inside the arrays. I started from there.
The name BinVal
comes from the fact that those numbers represent bin edges for
data binning/categorization.
From Aeson, the most important function to me would be the decode
:
As discussed in the tutorial and seen from the type information, parseJSON
is
another important function. At this point I knew that a good start would be
making the function decode
to work for BinVal
:
I needed to make BinVal
a member of the type class FromJSON
and here was the
part that took me a lot of time.
OK, understood something here. I wanted to handle the numeric case of Value
and it seemed that I needed to return something of the type Parser BinVal
.
I spent a lot of time messing around Json.withScientific
trying to make
parseJSON
return a Parser BinVal
and I was really lost with the types and the
tutorial examples.
This gist helped me to understand that
handling a specific subtype of Value
didn’t need to be complicated, my
parseJSON
could start like this:
Cool, I didn’t need Json.withScientific
.
Hoogle also helped to find
the function floatingOrInteger
of the package scientific. The code was
going somewhere:
I felt I was very close, but I still didn’t know how to generate a Parser
. I
thought about many things, tried to imagine some kind of recursion or some other
complicated typed messed stuff. Seeing the mention of mapM
in the tutorial
made everything even more confusing at the beginning, but it actually pointed me
to the right direction.
I had noticed that Parser
was some kind of type “container”, but I didn’t
realized it was a monadic type (actually, I didn’t read the tutorial slowly
enough to pay attention to this). After noticing the mapM
part, I knew that I
needed to put a BinValF f
and a BinValI i
inside the monadic type Parser
.
I started to carefully read again the tutorial. The amount of times I saw
return
made me wonder about its type signature:
Voilà! That is the answer! return
could take a type and wrap it with a monadic
container.
Complete parser
From the tutorial’s section
“Unknown field names”,
I defined the complete structure of my JSON file as the simple type alias
Bins
:
Cool! We are ready to go!
The complete solution looked like this: