Pickler Combinators in Elm

There was a discussion started by Levi in the elm slack, about Pickler Combinators. The paper is here:

Picklers in Haskell are like json encoders in Elm. The idea behind the pickler combinators, is that you use them to describe the structure of the data model, and from that description the encoders and decoders are automatically derived. This means you only have to describe the structure once, instead of writing as you often do a pair of encoding/decoding functions independently.

The idea had occurred to me before I heard of this pickler combinators paper, and I had a go at implementing it. Here it is:

I was hoping to go even further, and also be able to derive a json schema automatically too, as well as decoders that verified their input against a json schema.

To give an example of what this would look like in code:

type alias ObjectSimpleFields =
    { a : String
    , b : Int
    , c : Float
    , d : Bool
    }

objectSimpleFieldsDecoder =
    object ObjectSimpleFields
        |> with (field "a" .a string)
        |> with (field "b" .b integer)
        |> with (field "c" .c number)
        |> with (field "d" .d boolean)
        |> build

The idea is that there would be a DSL for describing the structure of whatever data model you are working with that pretty much mirrors its structure 1:1. From that you obtain some kind of Builder representing that model, and that could then be converted to a json encoder or decoder or whatever.

What I managed to do so far was pretty hard going, and I am aware that I still have union types, recursion, tuples, lists and so on to tackle.

It would be interesting if anyone has some good ideas on how to progress this?

Also, with the picklers paper above it might be much easier to see how this can be done in Elm (it suggests how an implementation in ML could be written). Or indeed, has anyone else already tried to do this in Elm?

2 Likes

Looking at the example code, it looks like the type alias is the part maintained by the user and the function below would be automatically derived by what you are implementing.

Further above you write that from that description the encoders and decoders are automatically derived.

Does that mean that the objectSimpleFieldsDecoder in the example code is just an intermediate representation the user never gets to see?

No, the type alias and the function are both maintained by the user. It might be nice if Elm could automatically generate the function, and this has been done apparently with similar work in Generic Haskell, whatever that is.

For Elm, I was thinking the user has to write the function, which will mostly just follow the structure of the data, but might diverge if they decide to customise things away from the most obvious representation.

In this case it outputs a Decoder directly without an intermediate representation.

This code is a work in progress, so it is a bit confusing, sorry. I first wrote a DSL for decoders, then tried to write exactly the same DSL for encoders, the idea being that these 2 DSLs could then be combined to output an Ecoder/Decoder pair. However, that seemed quite hard to pull off, so then I started down the route of having an intermediate representation (the Builder). So it might go DSL -> Builder -> Encoder/Decoder(/JsonSchema/XML/Whatever).

1 Like

I tried to think through how this would work too, and had a similarly difficult time. I think this is because the encode and decoder APIs are so different from each other.
I considered creating a document representation too, but I didn’t get very far. When I have time, I hope to revisit this though. Building up symmetric serializers seems much less error prone this way.

Good there are a few of us thinking along these lines; we’ll crack it eventually.

zwilias experiment with Encoders is also worth looking at: