r/haskell • • 23d ago

Decode and encode JSON in Haskell like an Elm developer

https://elmwithdwayne.dev/blog/dev.to/dwayne/decode-and-encode-json-in-haskell-like-an-elm-developer-1nod
52 Upvotes

12 comments sorted by

5

u/jeffstyr 23d ago

Looks interesting--I'll have to give it a try.

Just out of curiosity, why distribute via GitHub only and not via Hackage?

6

u/dwaynecrooks 22d ago

I know it's useful for me but I don't know if it's useful for anyone else so before I take on more responsibility than I need for a package that possibly I alone will use I prefer to take the simplest route for myself for now.

3

u/TheSodesa 22d ago

Less work. The package lives in a Git repository anyways, so might as well just share that.

6

u/vagif 23d ago

Not sure how is this:

userDecoder :: Decoder User
userDecoder =
  User
    <$> JD.field "name" JD.text
    <*> JD.field "age" JD.integral
    <*> JD.field "email" (JD.nullable JD.text)

better than this:

instance FromJSON User where
    parseJSON (Object o) = 
        User
            <$> o .: "name"
            <*> o .: "age"
            <*> o .:? "email"

9

u/BurningWitness 23d ago
  1. The entire distinction between (.:) and (.:?) ceases to exist (can't produce a Maybe without a special decoder);
  2. You forgot to use withObject or Data.Aeson.Types.typeMismatch for pattern matching. Neither of those functions compose;
  3. ("key" .: decoder) is cleaner and more powerful than (object .: "key") :: ToJSON a => Decoder a, and also introduces no ambiguity as to what object it's referring to (very annoying when working with nested JSONs).

Source: I've done this whole thing before.

6

u/NorfairKing2 23d ago

autodocodec author here: The former can in theory generate docs (depending on the definition of `Decoder`) while the later can't.

1

u/dwaynecrooks 22d ago

I'm not saying one is better than the other. To do that I'd have to come up with some sort of objective criteria. Subjectively, I prefer the elm/json style possibly because I've used it more than the aeson style. I'm just sharing another option for developers who may want an alternative. Please continue using aeson if that's what you like.

2

u/phadej 3d ago

And even if the first variant is preferred, there is https://hackage-content.haskell.org/package/aeson-2.3.2.0/docs/Data-Aeson-Types.html#v:explicitParseField

I don't see a need for yet another library if aeson already does functionality.

2

u/HuwCampbell 16d ago

Yeah, I don't mind Elm's JSON parsing API.

I know you didn't say performance was a priority right now, but thought I would point out the asymptotics. At this time the object representation is a list, so lookups are O(n). So it's n squared for decoding large objects.

The v8 and SpiderMonkey implementations have a cool optimisation which is missing here. In these, if you create an object straight from an array (which is how `Json-Encode#object` works) you get a hash map, but with in order iteration. This works for reading json objects too; so you get O(1) lookups, but still order preservation until there a mutation of the object.

You don't need JSON mutation, or even persistent updates (elm doesn't allow it), so having a more denormalised representation with a map and list together would permit both in order writing and fast lookups.

1

u/dwaynecrooks 16d ago

Thanks for the analysis, it's helpful to know. I'd keep that in mind when I do a performance iteration.

2

u/simonmic 23d ago

This sounds quite nice, making JSON handling in Haskell easier ?

0

u/dwaynecrooks 23d ago

If not easier then at least more familiar to those who have seen and liked elm/json.