# \`enumerate\` function for non-infinite custom types - proposal

**URL:** <https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636>\
**Category:** Request Feedback\
**Created:** [November 28, 2018, 3:27pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636 "2018-11-28T15:27:18Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Janiczek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/janiczek/32/4516_2.png) [@Janiczek](https://discourse.elm-lang.org/u/Janiczek)\
**Post date:** [November 28, 2018, 3:27pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/1 "2018-11-28T15:27:18Z")

</div>

# tl;dr

I propose the compiler would expose a “magical” function `enumerate : Type a -> List a` (for lack of better syntax) that would enumerate non-infinite types into a list:

```elm
type MyType
    = Foo
    | Bar Baz

type Baz
    = A
    | B
    | C

enumerate Baz
--> [A, B, C]

enumerate MyType
--> [Foo, Bar A, Bar B, Bar C]

```

* * *

# Motivation

I’m regularly encountering situations where I have a custom type for which I need all its values to be processed (shown, aggregated, …). For example [this thread](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964).

Currently there is no way to enumerate such types, so I need to create the list manually. The danger of that is that when I extend the custom type, I forget to add a value to that manually created list.

With the `enumerate` function I’d be able to do this and be absolutely sure that I can’t forget about adding a new value anywhere:

```elm
parser : Parser (Route -> a) a
parser =
    enumerate Route
        |> List.map routeToParser
        |> Url.Parser.oneOf

routeToParser : Route -> Parser (Route -> a) a
routeToParser route =
    case route of
        NotFound -> notFound
        Home -> home

```

* * *

# Ways to create the list semi-safely

## a) introduce a redundant definition that uses `case ... of` to warn me that I need to add a new value

```elm
parser : Parser (Route -> a) a
parser =
    Url.Parser.oneOf
        [ notFound
        , home
        ]

{-| This is only here to make sure I don't forget to add the new route's parser.
-}
parserFailsafe : Route -> String
parserFailsafe route =
    case route of
        NotFound ->
            "yes I have added the new parser to `parser` above"

        Home ->
            "yes I have added the new parser to `parser` above"

```

## b) create the list of values myself with a `case ... of` and slightly convoluted logic

```elm
nextType : Route -> Maybe Route
nextType : route = 
  case route of 
    NotFound -> Just Home
    Home -> Nothing

makeAllRoutes : Maybe Route -> List Route
makeAllRoutes mbroute = 
  case mbroute of
    Nothing -> []
    Just route -> route :: makeAllRoutes (nextType route)

allRoutes : List Routes
allRoutes = makeAllRoutes NotFound

```

I’ve come to realize this one doesn’t have any benefit over the first option and is slightly more complicated.

* * *

# Concerns

**Q:** what about (effectively) infinite types, ie. `Maybe Int` or this ⬇ ?

```elm
type MyType
    = Foo
    | Bar Int

```

**A:** The compiler would simply not allow using `enumerate` on that type. Would throw eg. a compiler error about infinite lists.

**Q:** What about parameterized types? eg. `Maybe a`  
**A:** The only allowed usages would be with concrete types:

- `enumerate (Maybe Bool)` would be OK
- `enumerate Maybe` wouldn’t
- `enumerate (Maybe a)` wouldn’t

**Q:** There is no support in the compiler for such reflection! (a `Type -> Value` function)  
**A:** Yeah, this would probably be the first such thing. But everything this function would need seems to be already in place / at least theoretically knowable.

- Is the type infinite? We can check that using set membership: is any of the “participating” types `Int`, `Float`, `String`, `Char`? (maybe I missed some?)
- Is the result of the `enumerate` call actually used anywhere? Dead code elimination tells us that.
- If the type is not infinite and the result is actually used, the compiler can generate a JS value with the actual list, potentially inline, potentially reusing it in multiple places.

* * *

# Pros

- Type safety for when you need to enumerate all values of a custom type (this is fairly common). This would eliminate the need to make the list yourself and possibly forget to add a value when you expand the custom type.

* * *

# Cons

- Slightly magical/non-explicit language feature
- Possibly hard to implement? I don’t really know.
- Doesn’t work on all types, but - well, there’s no way it possibly could in a strict language. But this limits the usefulness of the function and the number of usecases where this can be used.

---

<div class="post-metadata">

**Author:** ![dillonkearns](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dillonkearns/32/1587_2.png) [@dillonkearns](https://discourse.elm-lang.org/u/dillonkearns)\
**Post date:** [November 28, 2018, 6:16pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/2 "2018-11-28T18:16:58Z")

</div>

We generate these automatically for GraphQL Enums in `dillonkearns/elm-graphql`: [https://github.com/dillonkearns/elm-graphql/blob/master/examples/src/Swapi/Enum/Episode.elm#L17-L25](https://github.com/dillonkearns/elm-graphql/blob/master/examples/src/Swapi/Enum/Episode.elm#L17-L25)

It’s really handy. But in the case of GraphQL enums, there are never any parameters so that simplifies it.

---

<div class="post-metadata">

**Author:** ![klazuka](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/klazuka/32/862_2.png) [@klazuka](https://discourse.elm-lang.org/u/klazuka)\
**Post date:** [November 28, 2018, 8:16pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/3 "2018-11-28T20:16:10Z")

</div>

In a similar way, I’d like to be able to enumerate just the “tag” portion of a custom type variant.

There are several places in my app where I define an entity that has different variants. e.g.

```auto
type Classifier
    = Regex { ... }
    | Drill { ... }
    | Javascript String
    | JsonPath String

```

Now let’s suppose that I want to show a dropdown where you can create a classifier by selecting one of these variants. I could have the dropdown’s backing list be a list of strings to be displayed to the user. In which case I have the same problem that @Janiczek described: I must remember to add to the list whenever a new variant is added to the `Classifier` type. And when the user actually makes a selection, I must pattern match on the string to figure out which variant to create.

I run into similar problems with URL structure and routing. Let’s suppose that I want to have a URL which takes you to an editor page configured to create one of these variants. For example, `#/classifiers/new/regex` would create a new “regex” classifier. Again, I need to pattern match on a string to construct the appropriate variant.

After having run into this problem several times, I’ve started creating a separate custom type which matches the original type but discards the runtime data associated with each variant. So for the `Classifier` type described above, I would also create a type like so:

```auto
type ClassifierTag
    = RegexTag
    | DrillTag
    | JavascriptTag
    | JsonPathTag

```

and I would also define a value which enumerates all of the possible tags:

```auto
allTags = [RegexTag, DrillTag, JavascriptTag, JsonPathTag, RemoteTag]

```

and define a mapping to/from strings:

```auto
tagToString : ClassifierTag -> String
stringToTag : String -> Maybe ClassifierTag

```

* * *

It would be nice if there was a way to annotate a custom type in such a way that the Elm compiler could synthesize the “tag” type and functions defined above. And in the cases that @Janiczek described, you could also synthesize a function that enumerates all variants of the main type, not just the tags.

---

<div class="post-metadata">

**Author:** ![albertdahlin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/albertdahlin/32/868_2.png) [@albertdahlin](https://discourse.elm-lang.org/u/albertdahlin)\
**Post date:** [November 29, 2018, 7:06am UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/4 "2018-11-29T07:06:09Z")

</div>

What about this idea: Introduce the `enum` keyword?

This would create a normal custom type but be restricted to finite types as well as creating the `enumerate` list generator.

```elm
enum MyType
    = Foo
    | Bar Baz

enum Baz
    = A
    | B
    | C

```

### Thoughts

I think the intent of the type is communicated in a clearer way which is very helpful when you are a team working on the same project.  
Sometimes the type declaration lives in one module but used all across the code base.

This could also be used as a pseudo-typeclass, similar to how `comparable` or `number` works today. However I can not really come up with a problem where that would be useful.

It might be harder to understand for beginners. We already have `type alias` and `type` which can be confusing at first.  
But then on the other hand, you still need to understand the concept of infinite vs finite types anyway if you want to use `enumerate`.

---

<div class="post-metadata">

**Author:** ![pit](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/pit/32/5389_2.png) [@pit](https://discourse.elm-lang.org/u/pit)\
**Post date:** [November 30, 2018, 10:07pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/5 "2018-11-30T22:07:59Z")

</div>

Hi @Janiczek, if you slightly change the signature of the `next...` functions in your example b) to `Maybe a -> Maybe a`, it’s possible to write a generic `enumerate` function:

```elm
type Baz
    = A
    | B
    | C

bazIterator : Maybe Baz -> Maybe Baz
bazIterator b =
    case b of
        Just A -> Nothing
        Just B -> Just A
        Just C -> Just B
        Nothing -> Just C

enumerate : (Maybe a -> Maybe a) -> List a
enumerate iterator =
    let
        addNextValue : List a -> List a
        addNextValue list =
            case list |> List.head |> iterator of
                Just nextValue ->
                    nextValue :: list |> addNextValue

                Nothing ->
                    list
    in
    addNextValue []

enumerate bazIterator
--> [A, B, C]

```

In [this Ellie](https://ellie-app.com/43ff4ky7BYra1) I added an example for something like your nested type `MyType`, but I doubt that this would scale to more complex types.

For @klazuka: with an iterator function signature of `Maybe a -> Maybe ( a, String )` it should be possible to write an extended version of `enumerate` which generates the list together with the two conversion functions:

```elm
extendedEnumerate : (Maybe a -> Maybe ( a, String )) -> ( List a, a -> String, String -> Maybe a )
extendedEnumerate iterator =
    ...

```

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [November 30, 2018, 10:33pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/7 "2018-11-30T22:33:57Z")

</div>

Ive tried to simplify a bit - let me know what you think ☀

```elm
-- Order!

type alias Order a =
    a -> Maybe a

enumerateFrom : a -> Order a -> List a
enumerateFrom previous toNext =
    case toNext previous of
        Just next ->
            next :: enumerateFrom toNext next

        Nothing ->
            []

-- Colors!

type Color
    = Blue
    | Green
    | Red
    | Yellow

alphabeticalOrder : Order Color
alphabeticalOrder color =
    case color of
        Blue ->
            Just Green

        Green ->
            Just Red

        Red ->
            Just Yellow

        Yellow ->
            Nothing

colors : List Color
colors =
    enumerateFrom Blue alphabeticalOrder

```

---

<div class="post-metadata">

**Author:** ![ianmackenzie](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ianmackenzie/32/86_2.png) [@ianmackenzie](https://discourse.elm-lang.org/u/ianmackenzie)\
**Post date:** [November 30, 2018, 10:54pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/8 "2018-11-30T22:54:23Z")

</div>

I think I like the idea of an `enum` keyword, and in that case you might not even need a special `enumerate` function…in the same way that

```auto
type alias Person =
    { firstName : String
    , lastName : String
    }

```

introduces both a `Person` type alias and an associated `Person` _function_ of type `String -> String -> Person`,

```auto
enum Currency
    = Usd
    | Eur
    | Cad
    | Jpy

```

could introduce both a `Currency` type and an associated `Currency` _value_ equal to

```auto
[Usd, Eur, Cad, Jpy]

```

so that you could write

```auto
List.map currencyString Currency

```

If you want to get a bit more clever, the `Currency` value could alternatively be equal to

```auto
{ values = [Usd, Eur, Cad, Jpy] }

```

so that you could write the slightly more natural-reading

```auto
List.map currencyString Currency.values

```

In fact maybe you don’t even need a separate `enum` keyword…maybe any `type CustomType` definition where every constructor is a plain value results in a corresponding `CustomType` value of type `{ values : List CustomType }`.

Caveat: either of these approaches would mean that you couldn’t have an enum of the form

```auto
type Something
    = Something
    | SomethingElse
    | AnotherThing

```

since then `Something` already exists as a value as well as a type. That seems kind of OK though - if there’s one ‘special’ or ‘default’ entry like that, then it seems less likely that it would make sense to map over all values in a way that treats them equally.

---

<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:** [December 10, 2018, 10:54pm UTC](https://discourse.elm-lang.org/t/enumerate-function-for-non-infinite-custom-types-proposal/2636/9 "2018-12-10T22:54:26Z")

</div>

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