# How to do enums in Elm

**URL:** <https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353>\
**Category:** Learn\
**Created:** [May 31, 2018, 2:54am UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353 "2018-05-31T02:54:15Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![chingwa](https://avatars.discourse-cdn.com/v4/letter/c/7ea924/32.png) [@chingwa](https://discourse.elm-lang.org/u/chingwa)\
**Post date:** [May 31, 2018, 2:54am UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/1 "2018-05-31T02:54:16Z")

</div>

TL;DR: I’ve made a working Ellie for this if you want to play around: [https://ellie-app.com/nt22pfXCtKa1](https://ellie-app.com/nt22pfXCtKa1)

I’ve seen a few comments around how working with enums suck in Elm. I believe much of the frustration comes from trying to re-implement in Elm a language feature that exists in other languages. As a result of applying the wrong kind of thinking, the wrong abstraction is reached and Elm gets the blame. So while I’m no expert, the following approach may lessen the pain that some people feel.

Let’s start with an example of the wrong way to do things, that, unfortunately, I have seen repeated often, e.g.  
hackernoon: a-small-dive-into-and-rejection-of-elm-8217fd5da235 and [http://reasonablypolymorphic.com/blog/elm-is-wrong/](http://reasonablypolymorphic.com/blog/elm-is-wrong/)

Elm has union types, so we can model an enum like this:

```auto
type Preference = NoPreference | Morning | Afternoon`

```

We need to show these values in a select list, so we need a text description of each preference option.

```auto
    displayName preference =
    	case preference of
    		NoPreference -> "No preference"
    		Morning -> "Morning"
    		Afternoon -> "Afternoon"

```

There’s no way to enumerate the values of a Preference, so we also need a list

```auto
preferences = [NoPreference, Morning, Afternoon]

```

Now we build our select list

```auto
preferenceOption preference = option [] [text <| displayName preference]

```

Using this we can display a select list to a user. Cool! Ok, but how do we get the value our user has selected? We can use the onInput function, but that produces a msg that has a string, so we need a function to convert the string to our Preference type for our msg.

```auto
type Msg = PreferenceSelected Preference

parsePreference string = 
	case string of 
		"Morning" -> Morning
		"Afternoon" -> Afternoon
		_ -> NoPreference
		
view model = 
    select [onInput (PreferenceSelected << parsePreference)]
	    (List.map preferenceOption preferences)

```

At this point everyone gives up because Elm is so repetitive, doesn’t have typeclasses, forces us to break type safety (with the \_) etc. etc. etc.

Let’s take a deep breath and start again. What is an enum anyway? Some languages have special keywords for this, Elm doesn’t, but what is it? Well, an enum is a list of values. Pretty simple. How should this be modelled in Elm?

```auto
List String

```

Yes, that’s it. Elm already has a way of modelling a list of values and we can get away with the bare minimum of code when using Elm’s built in List type. Continuing our example we have the following:

```auto
preferenceEnum = ["No preference", "Morning", "Afternoon"]

type Msg = PreferenceSelected String

-- View
preferenceOption preference = option [] [text preference]

view model = 
    select [onInput PreferenceSelected]
        (List.map preferenceOption preferenceEnum)

```

I would wager that the approach above would be enough for some. Yes, I know that using a string isn’t particularly type safe, but we need to understand our domain a lot more before we do anything more advanced than this. Using a type safe language does not mean we have to hit type safe perfection before we can get any work done.

For the sake of argument, lets see if we can improve the type safety a little bit. One thing we could do is tag our string

```auto
type Preference = Preference String

```

If you’re worried about rogue code generating values of this type, you could hide the constructor in a module and make Preference an opaque type. Either way, the definition of the enum changes slightly:

```auto
preferenceEnum = 
    [ Preference "No preference"
    , Preference "Morning"
    , Preference "Afternoon" 
    ]

```

Notice we’re still using a list as the core concept of what an enum is: a list of values. We’re fiddling around with what the values are but the enum is still essentially the same thing.

For display purposes we’ll need a helper function to get our string back out.

```auto
preferenceToString preference =
	case preference of
		Preference string -> string

```

Or as I’ve seen written elsewhere:

```auto
preferenceToString (Preference preference) = preference

```

And the rest of the code has minor changes too:

```auto
type Msg = PreferenceSelected Preference

preferenceOption preference = option [] [text <| preferenceToString preference]

select [onInput PreferenceSelected]
	(List.map preferenceOption preferenceEnum)

```

All good. Almost. We’ve broken our select handler by using our tagged type rather than a string. Instead of jumping straight into writing a decoder, the reverse of `preferenceToString`, we should stop and think for a bit. If an enum is a list of values, and those values have unique string representations (directly or with a `toString` function), then we should be able to get back the value from the string using a reverse lookup rather than a new custom decoder for every enum that we could write.

```auto
findEnumValue : List a -> (a -> String) -> String -> Maybe a
findEnumValue enum enumToString value =
    enum
    |> List.filter ((==) value << enumToString)
    |> List.head

```

Notice that this is rather generic and doesn’t care what values are in the enum. Writing a decoder from this is quite straightforward:

```auto
decodeEnumValue : List a -> (a -> String) -> String -> Decoder a
decodeEnumValue enum enumToString stringValue =
		case findEnumValue enum enumToString stringValue of
			Just value ->
				Decode.succeed value

			Nothing ->
				Decode.fail <| "Could not decode value to enum: " ++ stringValue

```

To round things off we could implement a generic enum input handler:

```auto
onEnumInput : List a -> (a -> String) -> (a -> msg) -> Attribute msg
onEnumInput enum enumToString tagger =
    let
        decodeTargetValue =
            targetValue
                |> Decode.andThen (decodeEnumValue enum enumToString)
                |> Decode.map tagger
    in
    on "input" decodeTargetValue

```

Now we’re done! If you really really wanted to, you could return to the definition of `Preference` at the start of this post, but the way forward is clear. Simply change your implementation of `preferenceToString` and the list of values in your enum and that’s it.

---

<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:** [May 31, 2018, 9:02am UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/2 "2018-05-31T09:02:18Z")

</div>

Very good.

With reference to “the expression problem”, tagged union types are complete and not dynamically extensible so they cannot be the right way to approach this. The way to make code behaviour extendable is always to pass a function. I notice that you have this in your functions where you pass in a `(a -> String)`:

```
findEnumValue : List a -> (a -> String) -> String -> Maybe a
findEnumValue enum enumToString value =
    enum
    |> List.filter ((==) value << enumToString)
    |> List.head

```

If you wanted to capture the whole Enum type as a single thing, you could do this:

```
type alias Enum a = 
   { values : List a
   , toString : a -> String
   }

```

Then all your function signatures would be in terms of enums:

```
findEnumValue : Enum a -> String -> Maybe a
decodeEnumValue : Enum a -> String -> Decoder a
onEnumInput : Enum a -> (a -> msg) -> Attribute msg
```

---

<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:** [May 31, 2018, 9:43pm UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/3 "2018-05-31T21:43:14Z")

</div>

Nice rundown of the pros and cons of strings vs enums! I enjoyed following along with your process as you implemented the string-based alternative 😃

I may be an outlier but I don’t mind writing serialization/deserialization logic for my types (including enums) 😮. It makes sense (to me) that data needs to be serialized/deserialized at the edges of the system. This includes HTML forms or JSON sent over the wire.

I typically define

`toString : MyType -> String`  
`fromString : String -> Result String MyType`

and often a decoder like:

```elm
decoder : Decoder MyType
decoder =
  JD.string |> JD.andThen (JD.fromResult << fromString)

```

The idea of having both `fromString` and `decoder` be derivable given a list of values and a `toString` function is fascinating 🤔. This should be possible with union type enums too. So far in my code though, these functions have been simple/short so I haven’t had pain pushing me to abstract but I can see why that might be appealing.

---

<div class="post-metadata">

**Author:** ![chingwa](https://avatars.discourse-cdn.com/v4/letter/c/7ea924/32.png) [@chingwa](https://discourse.elm-lang.org/u/chingwa)\
**Post date:** [June 1, 2018, 12:19am UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/4 "2018-06-01T00:19:29Z")

</div>

Thanks for the comments. 😃

I agree with your Enum type alias approach. It improves readability quite a bit!

---

<div class="post-metadata">

**Author:** ![chingwa](https://avatars.discourse-cdn.com/v4/letter/c/7ea924/32.png) [@chingwa](https://discourse.elm-lang.org/u/chingwa)\
**Post date:** [June 1, 2018, 12:24am UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/5 "2018-06-01T00:24:08Z")

</div>

Thanks Joel! 😃

Good point on the union type enums - it should work fine with them too.

---

<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 1, 2018, 9:14am UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/6 "2018-06-01T09:14:27Z")

</div>

> [@chingwa](#):
>
> I agree with your Enum type alias approach. It improves readability quite a bit!

One thing to be aware of, if you use that approach you may end up with functions in your model - which works just fine, but some people try to avoid doing it.

---

<div class="post-metadata">

**Author:** ![mfeineis](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mfeineis/32/104_2.png) [@mfeineis](https://discourse.elm-lang.org/u/mfeineis)\
**Post date:** [June 1, 2018, 2:48pm UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/7 "2018-06-01T14:48:12Z")

</div>

Functions are problematic for the `==` operator, a comparison of functions or a record that has functions inside can cause a runtime exception. Also, records should more often than not be serializable data just for the sake of people’s sanity (and `Debug.log`). If you put functions inside the records then the record contains logic, which is kind of against pure functions and data argument. Don’t get me wrong, I can totally see the utility in this approach but it has downsides and I probably wouldn’t use that outside of pet projects 🤷‍♀️

---

<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 1, 2018, 3:10pm UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/8 "2018-06-01T15:10:05Z")

</div>

> [@mfeineis](#):
>
> Functions are problematic for the `==` operator…

All good points 👍, I have some thoughts around this but will save for another thread so as not to derail the enums discussion.

---

<div class="post-metadata">

**Author:** ![rgrempel](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rgrempel/32/93_2.png) [@rgrempel](https://discourse.elm-lang.org/u/rgrempel)\
**Post date:** [June 1, 2018, 5:27pm UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/9 "2018-06-01T17:27:57Z")

</div>

It’s not clear to me that using the type alias approach would end up with functions in the **model**.

> [@rupert](#):
>
> ```auto
> type alias Enum a = 
> { values : List a
> , toString : a -> String
> }
> 
> ```

If I understand it correctly, the `values` here is not some subset of `a`, but instead is all the possible values of `a`. So, it doesn’t change over time, and can be defined (for a given `a`) once, as a singleton. It doesn’t look like something you’d put in your model.

You might have an `a` in your model, or even a `List a`, but that’s a different matter.

---

<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 2, 2018, 9:23pm UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/10 "2018-06-02T21:23:27Z")

</div>

> [@rgrempel](#):
>
> It’s not clear to me that using the type alias approach would end up with functions in the **model**.

It wasn’t clear to me either, so I said it _may_ end up in the model. But I think you are right, this definition only really needs to go at the top-level for an enum.

Also I can’t see why we would need to to `enum1 == enum2`, but if we really did we could do `enum1.values == enum2.value`, and also `Debug.log "enums" enum.values`, so I guess bundling them together does not really have to be a problem.

---

<div class="post-metadata">

**Author:** ![wizza-smile](https://avatars.discourse-cdn.com/v4/letter/w/6a8cbe/32.png) [@wizza-smile](https://discourse.elm-lang.org/u/wizza-smile)\
**Post date:** [June 3, 2018, 3:56pm UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/11 "2018-06-03T15:56:14Z")

</div>

somebody build the enum package already 🙂

---

<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:** [June 13, 2018, 3:56pm UTC](https://discourse.elm-lang.org/t/how-to-do-enums-in-elm/1353/12 "2018-06-13T15:56:18Z")

</div>

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