# Encoding/Decoding Recursive Types with miniBill/elm-codec

**URL:** <https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898>\
**Category:** Show and Tell\
**Created:** [June 28, 2019, 4:23pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898 "2019-06-28T16:23:49Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![ChrisWellsWood](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chriswellswood/32/5293_2.png) [@ChrisWellsWood](https://discourse.elm-lang.org/u/ChrisWellsWood)\
**Post date:** [June 28, 2019, 4:23pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/1 "2019-06-28T16:23:49Z")

</div>

I just want to tell people about my experience using the `elm-codec` package made by miniBill:

[https://package.elm-lang.org/packages/miniBill/elm-codec/latest/](https://package.elm-lang.org/packages/miniBill/elm-codec/latest/)

This package was recently highlighted in Elm Weekly so I decided to give it a spin. The selling point was simultaneously defining both the encoder and decoder, which is convenient, and after enjoying using for some simple stuff, I decided to try and use it to write the encoders/decoders for some recursive types, which I’ve been dreading. The data type looks like this:

```elm
type Requirement a
    = Data a
    | Not (Requirement a)
    | Or (Requirement a) (Requirement a)
    | And (Requirement a) (Requirement a)
    | Any (List (Requirement a))
    | All (List (Requirement a))

```

`elm-codec` has some tools for dealing with this type of data structure. The codec, which you use to create the encoder and decoder, is made using the `Codec.recursive` and `Codec.custom` functions. Here’s the code for my codec:

```elm
requirementCodec : Codec a -> Codec (Requirement a)
requirementCodec meta =
    Codec.recursive
        (\rmeta ->
            Codec.custom
                (\fdata fnot for fand fany fall value ->
                    case value of
                        Data data ->
                            fdata data

                        Not requirement ->
                            fnot requirement

                        Or requirement1 requirement2 ->
                            for requirement1 requirement2

                        And requirement1 requirement2 ->
                            fand requirement1 requirement2

                        Any requirements ->
                            fany requirements

                        All requirements ->
                            fall requirements
                )
                |> Codec.variant1 "Data" Data meta
                |> Codec.variant1 "Not" Not rmeta
                |> Codec.variant2 "Or" Or rmeta rmeta
                |> Codec.variant2 "And" And rmeta rmeta
                |> Codec.variant1 "Any" Any (Codec.list rmeta)
                |> Codec.variant1 "All" All (Codec.list rmeta)
                |> Codec.buildCustom
        )

```

It was really quick to get this up and running, the compiler (as always) held my hand. The JSON that it produces is sensible and easy to work with on the back end.

I don’t know the author or anything, just wanted to highlight a very positive experience that I had using a module!

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [June 28, 2019, 7:47pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/2 "2019-06-28T19:47:26Z")

</div>

I knew about it from Slack, and also some previous discussions on here over a year ago ([Is it possible to capture type information about records?](https://discourse.elm-lang.org/t/is-it-possible-to-capture-type-information-about-records/1169)). I have been using `miniBill/elm-codec` in my latest project and finding it very convenient and a significant reduction in boilerplate involved in writing encoders and decoders. I would also recommend this package to anyone needing to write encoders and decoders.

---

<div class="post-metadata">

**Author:** ![miniBill](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/minibill/32/800_2.png) [@miniBill](https://discourse.elm-lang.org/u/miniBill)\
**Post date:** [June 29, 2019, 10:36pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/3 "2019-06-29T22:36:35Z")

</div>

Took me a while between that posts and publishing the library, eh? 😂

@ChrisWellsWood: thanks for the positivity! Any pain points or missing features?

One thing that I totally need to add is support for mutually recursive data types. (I was thinking along the lines of `recursive2`)

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [June 30, 2019, 2:36pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/4 "2019-06-30T14:36:06Z")

</div>

I had an example where I needed mutually recursive data types (but I think the decoder only needed to be recursive in one of them).

The model on the back-end was something like this:

```
type ContentModle
    = ContentModel      
      { type _ : ContentType
      , otherFields : ...
      }

 type ContentType
   = ContentType
     { model : ContentModel
     , otherFields : ...
     }

```

It was for a CMS where the content could be made up from a tree of pieces of content. Typically a whole page at a time was requested from the server, sometimes one page would be just 1 level deep, but sometimes several levels, and the back end could recurse down and fetch 1 complete page at a time built up in this way.

I had to use custom types instead of type aliases, as a type alias does not allow mutual recursion.

Haven’t tried to write an `elm-codec` for this, but if its a use case you think you can handle, I think there are places where it is sometimes needed.

---

<div class="post-metadata">

**Author:** ![ChrisWellsWood](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chriswellswood/32/5293_2.png) [@ChrisWellsWood](https://discourse.elm-lang.org/u/ChrisWellsWood)\
**Post date:** [July 1, 2019, 8:08am UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/5 "2019-07-01T08:08:05Z")

</div>

I suppose the only thing that was a slight pain point was needing asymmetric encoders and decoders, for example when I’m caching to local storage and I don’t need all the data on a record, where the fields that weren’t cached have default values. I ended up making a cached version of the record and made some functions to convert between, which works fine and is probably better in the long run, but I thought I’d be able to do something like this:

```elm
storedCodec : Codec Specification
storedCodec =
    Codec.object
        (\stored ->
            { name = stored.name
            , description = stored.description
            , requirements = stored.requirements
            , deleteStatus = Common.Unclicked
            }
        )
        |> Codec.field "name" .name Codec.string
        |> Codec.field "description" .description Codec.string
        |> Codec.field "requirements" .requirements (requirementCodec requirementDataTypeCodec)
        |> Codec.buildObject

```

---

<div class="post-metadata">

**Author:** ![miniBill](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/minibill/32/800_2.png) [@miniBill](https://discourse.elm-lang.org/u/miniBill)\
**Post date:** [July 1, 2019, 9:05am UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/6 "2019-07-01T09:05:19Z")

</div>

You can either use `Codec.map` to map your “memory” type to your “persistent” type and back, or use `Codec.constant` like this: [https://ellie-app.com/5XQ6YxvRpjja1](https://ellie-app.com/5XQ6YxvRpjja1)

---

<div class="post-metadata">

**Author:** ![ChrisWellsWood](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chriswellswood/32/5293_2.png) [@ChrisWellsWood](https://discourse.elm-lang.org/u/ChrisWellsWood)\
**Post date:** [July 1, 2019, 9:23am UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/7 "2019-07-01T09:23:08Z")

</div>

Ah, this is great! I knew there must be a way.

---

<div class="post-metadata">

**Author:** ![ChrisWellsWood](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chriswellswood/32/5293_2.png) [@ChrisWellsWood](https://discourse.elm-lang.org/u/ChrisWellsWood)\
**Post date:** [July 5, 2019, 7:46pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/8 "2019-07-05T19:46:36Z")

</div>

I’ve just went through my code and replaced all the unnecessary persistent types in favour of using `constant` and `map`. It’s all worked, which is awesome, but I had a thought while doing it: Is `map` the right name for that function? I would have assumed it had the normal map type signature of `(a -> b) -> Codec a -> Codec b`, which is why I didn’t look at it too closely the first time I looked through the docs (for other readers: it has the type signature `(a -> b) -> (b -> a) -> Codec a -> Codec b`). Do you think it’s worth renaming this function to stop confusion and help draw attention to its use?

---

<div class="post-metadata">

**Author:** ![miniBill](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/minibill/32/800_2.png) [@miniBill](https://discourse.elm-lang.org/u/miniBill)\
**Post date:** [July 6, 2019, 9:31am UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/9 "2019-07-06T09:31:11Z")

</div>

_Technically_ `Codec` is an Invariant Functor, and `map` should be called `invmap`: [https://hackage.haskell.org/package/invariant-0.5.3/docs/Data-Functor-Invariant.html#v:invmap](https://hackage.haskell.org/package/invariant-0.5.3/docs/Data-Functor-Invariant.html#v:invmap)  
Ototh I don’t think that `invmap` would be more helpful that `map` as a name.

I must definitely document the `constant` and `map` examples tho.

---

<div class="post-metadata">

**Author:** ![ChrisWellsWood](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chriswellswood/32/5293_2.png) [@ChrisWellsWood](https://discourse.elm-lang.org/u/ChrisWellsWood)\
**Post date:** [July 7, 2019, 12:13pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/10 "2019-07-07T12:13:41Z")

</div>

That’s interesting, I’ve never heard of an invariant functor before. Like you said `invmap` is not going to be helpful to most people, maybe a more human readable name like `twoWayMap` or something along those lines?

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [July 17, 2019, 12:13pm UTC](https://discourse.elm-lang.org/t/encoding-decoding-recursive-types-with-minibill-elm-codec/3898/11 "2019-07-17T12:13:47Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
