# Decoder generator and imports cleanup tool

**URL:** <https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399>\
**Category:** Show and Tell\
**Created:** [June 19, 2018, 11:07am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399 "2018-06-19T11:07:15Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 19, 2018, 11:07am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/1 "2018-06-19T11:07:15Z")

</div>

I’ve written two small tools to help clean up code / automate the mindless parts of coding:

[Decoder generator](https://dkodaj.github.io/decgen)  
Generates JSON decoders and encoders from type definitions.

[Imports cleanup tool](https://dkodaj.github.io/impfix)  
Removes unused imports and makes “exposing (…)” imports explicit.

Just copy/paste your code and press the button.

This project may have benefitted from [elm-ast](https://github.com/Bogdanp/elm-ast) or [elm-parser](https://github.com/michaeljones/elm-parser), but I was pretty much done when I discovered those.

Feedback welcome!

---

<div class="post-metadata">

**Author:** ![joelq](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joelq/32/445_2.png) [@joelq](https://discourse.elm-lang.org/u/joelq)\
**Post date:** [June 19, 2018, 2:42pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/2 "2018-06-19T14:42:04Z")

</div>

I like the look and feel of it! I threw a bunch of types at the decoder generator to see what it could handle 😆. I’m impressed 💯 !

It handles:

- ✅ Records
- ✅ Tuples
- ✅ Dicts
- ✅ Lists
- ✅ Nested structures like a list of tuples
- ✅ Sum types (e.g. `type Color = Red | Green | Blue`)

I’ve noticed is struggles with product types like `type Email = Email String`. If I were writing this by hand I’d implement the decoder as this one-liner:

```elm
decodeEmail =
  Dec.map Email Dec.string

```

but the generator attempts to decode it using a huge nested case and multiple calls to `decodeValue` 😰

```elm
decodeEmail =
   let
      recover xs =
         case xs of
            a0::bs->
               case decodeValue Dec.string a0 of
                  Ok "Email"->
                     case bs of
                        a1::cs ->
                              case decodeValue Dec.string a1 of
                                 Ok a1_->
                                    Dec.succeed <| Email a1_
                                 Err err->
                                    Dec.fail err
                        _->
                           Dec.fail <| "Invalid fields for constructor Email: " ++ toString bs
                  Ok other->
                     Dec.fail <| "Invalid constructor field found: " ++ other
                  Err err->
                     Dec.fail err
            _->
               Dec.fail "Invalid JSON input: empty list"
   in
      Dec.list Dec.value |> andThen recover

```

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 19, 2018, 3:14pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/3 "2018-06-19T15:14:07Z")

</div>

Txx for the quick feedback!

This is ugly indeed, I’ll fix it up…

---

<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 19, 2018, 4:01pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/4 "2018-06-19T16:01:44Z")

</div>

> [@dkodaj](#):
>
> Removes unused imports and makes “exposing (…)” imports explicit.

Nice work.

I would find this more usable if it were available as a command line tool, it works nicely but not realistic to cope paste every file in a large project.

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 19, 2018, 4:44pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/5 "2018-06-19T16:44:33Z")

</div>

> I’ve noticed is struggles with product types like `type Email = Email String`

This is fixed now.

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 19, 2018, 4:45pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/6 "2018-06-19T16:45:48Z")

</div>

> I would find this more usable if it were available as a command line tool, it works nicely but not realistic to cope paste every file in a large project.

I agree. I’ll try to do that if the copy/paste version doesn’t uncover any obvious bugs.

---

<div class="post-metadata">

**Author:** ![joelq](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joelq/32/445_2.png) [@joelq](https://discourse.elm-lang.org/u/joelq)\
**Post date:** [June 19, 2018, 5:30pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/7 "2018-06-19T17:30:45Z")

</div>

> [@dkodaj](#):
>
> > I’ve noticed is struggles with product types like `type Email = Email String`
> 
> This is fixed now.

That was quick! 🎉 I notice it works for multi-argument product types too 💯

I figured I’d throw something a bit more complex at it:

```elm
type Suit = Hearts | Spades
type Rank = Ace | King | Queen

type Card = PlayingCard Suit Rank

```

It’s able to correctly generate decoders for this! 👍

Trying something fancier, I notice it starts to breakdown when combining sum and product types. When I extended the example above to add jokers:

```elm
type Suit = Hearts | Spades
type Rank = Ace | King | Queen

type Card
  = PlayingCard Suit Rank
  | Joker

```

then it’s back to generating the deeply nested `decodeValue` stuff.

---

<div class="post-metadata">

**Author:** ![Chadtech](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chadtech/32/609_2.png) [@Chadtech](https://discourse.elm-lang.org/u/Chadtech)\
**Post date:** [June 19, 2018, 6:00pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/8 "2018-06-19T18:00:21Z")

</div>

Pretty cool! I am always using elm-analyse to find unused imports, but I can never quite get rid of all of them, so having an automatic tool would be super useful for me. Thanks for working on this.

However, I did apply the unused import tool to four big modules of mine, and I had a lot of problems:

0 It would delete this import: `import Data.Id exposing(Id(Id, New))`, where the module contained…

```auto
type Id
    = Id String
    | New

```

1 I had an import that was like `import Data.A as A exposing (B)`, and I had functions that used `B` in both aliased and unaliased forms like `String -> A.B -> ...` and `String -> B` and it removed the import for `B` entirely

2 I import a union type and expose many constructors like `import Tracking exposing (Event(A,B,C))` and it would format that to `import Tracking exposing (A, B, C)`

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 20, 2018, 8:06am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/9 "2018-06-20T08:06:34Z")

</div>

Thanks, these are very helpful.

I’ll need to work on handling exposed types. That should take care of issues 0 and 2.

I haven’t been able to reproduce issue 1. The following yields `import Data.A as A exposing (B)`:

```elm
import Data.A as A exposing (B)

myFunc1: String -> A.B -> Int
myFunc1 a b =
  5

myFunc2: String -> B
myFunc2 a =
  b

```

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 20, 2018, 12:53pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/10 "2018-06-20T12:53:58Z")

</div>

> [@joelq](#):
>
> ```auto
> type Card
> = PlayingCard Suit Rank
> | Joker
> 
> ```
> 
> then it’s back to generating the deeply nested `decodeValue` stuff.

This should work now. The result is the following:

```elm
decodeCard =
   Dec.field "Constructor" Dec.string |> andThen decodeCardHelp

decodeCardHelp constructor =
   case constructor of
      "PlayingCard" ->
         decode
            PlayingCard
               |> required "A1" decodeSuit
               |> required "A2" decodeRank
      "Joker" ->
         Dec.succeed Joker
      other->
         Dec.fail <| "Unknown constructor for type Card: " ++ other

```

Thanks for raising this, it simplified the code quite a bit. Let me know if anything else comes up!

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 29, 2018, 10:00am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/11 "2018-06-29T10:00:10Z")

</div>

The 3 issues you mentioned should new be OK (hope). You can try the [copy/paste version](https://dkodaj.github.io/impfix/); I’ll be back with a command-line tool soon.

---

<div class="post-metadata">

**Author:** ![simonh1000](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/simonh1000/32/100_2.png) [@simonh1000](https://discourse.elm-lang.org/u/simonh1000)\
**Post date:** [June 30, 2018, 11:00am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/12 "2018-06-30T11:00:44Z")

</div>

Do you have any plans to make the cleanup tool work from the command line, so that we could add it to build scripts?

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [June 30, 2018, 11:47am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/13 "2018-06-30T11:47:26Z")

</div>

Yes, that would be the idea. I’m working on an npm package, should be ready in a few days.

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [July 3, 2018, 7:49am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/14 "2018-07-03T07:49:40Z")

</div>

Here’s a command-line version of the cleanup tool:  
[https://www.npmjs.com/package/elm-impfix](https://www.npmjs.com/package/elm-impfix)

---

<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:** [July 3, 2018, 2:52pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/15 "2018-07-03T14:52:13Z")

</div>

I don’t mean to be demanding because you’ve done a great job here. Is it possible for it to just recursively replace all .elm files under a specified folder directly? Rather than to write new files with ‘-Impfix.elm’ in the name?

Does not have to be the default behaviour and I can see why you would be careful when writing a new tool, as you don’t want to accidentally obliterate some existing .elm code that has not been checked in yet. On the other hand, we all know how to use git and revert files if something goes wrong - so I would feel quite safe running it recursively on a large project and seeing if I like the results.

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [July 3, 2018, 4:49pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/16 "2018-07-03T16:49:24Z")

</div>

Noprob about the demands, I’m happy to contribute. I’ll look into it!

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [July 4, 2018, 8:41am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/17 "2018-07-04T08:41:41Z")

</div>

I published a new patch. You can recursively overwrite all files by

```auto
$ elm-impfix "folder/**/*.elm" -r

```

I also fixed a few bugs that came up when I accidentally ran this command on an elm-stuff folder (not recommended).

---

<div class="post-metadata">

**Author:** ![hopelessly](https://avatars.discourse-cdn.com/v4/letter/h/ee7513/32.png) [@hopelessly](https://discourse.elm-lang.org/u/hopelessly)\
**Post date:** [July 6, 2018, 9:44am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/18 "2018-07-06T09:44:26Z")

</div>

This is a great tool. Is there any chance of a option to Decode with out needing the `Json.Decode.Pipeline` package and instead use the core decoders only?

---

<div class="post-metadata">

**Author:** ![dkodaj](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dkodaj/32/1041_2.png) [@dkodaj](https://discourse.elm-lang.org/u/dkodaj)\
**Post date:** [July 6, 2018, 11:21am UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/19 "2018-07-06T11:21:56Z")

</div>

Thanks!

I think that’s fairly easy to do; I’ll check. I used `Pipeline` because you can decode any number of fields whereas the `Dec.map`'s run out at 8. But I guess one could make `Pipeline` as a last-resort option for cases when there are more than 8 fields.

---

<div class="post-metadata">

**Author:** ![hopelessly](https://avatars.discourse-cdn.com/v4/letter/h/ee7513/32.png) [@hopelessly](https://discourse.elm-lang.org/u/hopelessly)\
**Post date:** [July 9, 2018, 1:35pm UTC](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399/20 "2018-07-09T13:35:58Z")

</div>

Or you could use `andMap` [http://package.elm-lang.org/packages/elm-community/json-extra/2.7.0/Json-Decode-Extra#andMap](http://package.elm-lang.org/packages/elm-community/json-extra/2.7.0/Json-Decode-Extra#andMap)

[Next page](https://discourse.elm-lang.org/t/decoder-generator-and-imports-cleanup-tool/1399.md?page=2)
