Join egghead, unlock knowledge.

Want more egghead?

This lesson is for members. Join us? Get access to all 3,000+ tutorials + a community with expert developers around the world.

Unlock This Lesson

Already subscribed? Sign In


    Use `andThen` to transform data while decoding JSON into Elm

    Enrico BuonannoEnrico Buonanno

    Some data transformations can be obtained by "tagging on" a function to a decoder with map. But if the transformation may fail, then we need a different approach: express failure in terms of decoding (if the transformation fails, decoding fails), and then tag this Decoder-returning function on to the previous Decoder with andThen. This is functional programming's good old bind, particularized for the Decoder type.



    Become a Member to view code

    You must be a Member to view code

    Access all courses and lessons, track your progress, gain confidence and expertise.

    Become a Member
    and unlock code for this lesson


    Instructor: 00:01 Let's imagine we now need an additional field that indicates the user's gender. In our JSON, this would be the string "male" or "female." Let me update my model in Elm, and I'll add a field to my user type alias.

    00:24 In my decoder, I need to use map4, because I have an additional field. Let me add the field here. The name of the field in the JSON is gender. I'll use gender, a new decoder that I write. I expect that this would be fairly similar to our decoder for membership, so let me just duplicate that, and now make the necessary changes.

    00:51 Let me call this value gender and its type is decoder of gender. Rather than starting with bool, I'll start with string, and then I'll map a function toGender, which I define here, and the type of toGender should be string to gender.

    01:33 Let's see if that works. It doesn't work, because my pattern matching is not exhaustive. There are infinitely many more strings than indicated for. What if I get another string that is not male or female? Then, I don't really know what to do. In other words, the decoding should really fail here.

    01:52 What I need to do now is to change the signature here. I cannot go from just any string to gender, but I can go from a string to a decoder of gender. If I get string male, I can succeed with male. Succeed is a function that returns a decoder that always succeeds with the given value.

    02:15 Here, I can succeed with female. Here, I'll fail with an error message. Fail returns a decoder that always fails with the given message. Let me try inaudible now. Now, I have a type mismatch, because string map with this function that returns a decoder, now returns a decoder of a decoder of gender.

    02:44 To flatten this decoder of a decoder into a single decoder, I need to use another function that's called andThen. If I say now, you see that everything works nicely, and my decoded user has a gender field with the value set to male.

    03:02 It's important to look at the difference. In the case of membership, we were able to use map, because we were mapping a function from bool to membership. In the case of gender, we were not able to write a function from string to gender, so we had to write a function from string to a decoder of gender. These two need to be glued together with andThen, rather than map.

    03:22 It can be useful to look at the repo or at the documentation and compare the signatures of map with the signature for andThen. You see it's essentially the same, but here we have a function from A to B, or rather from A to value, and here we have a function from A to a decoder of B. The rest is the same.

    03:47 You see exactly the same pattern with other types. For example, if you compare a with maybe.andThen, again, here we have a function from A to B, and here function from A to the maybe of B. Of course, in functional programming, functions with this sort of signatures would be called bind.