|
| 1 | +# Comparing Custom Types |
| 2 | + |
| 3 | +The built-in comparison operators work on a fixed set of types, like `Int` and `String`. That covers a lot of cases, but what happens when you want to compare custom types? |
| 4 | + |
| 5 | +This page aims to catalog these scenarios and offer alternative paths that can get you unstuck. |
| 6 | + |
| 7 | + |
| 8 | +## Wrapped Types |
| 9 | + |
| 10 | +It is common to try to get some extra type safety by creating really simple custom types: |
| 11 | + |
| 12 | +```elm |
| 13 | +type Id = Id Int |
| 14 | +type Age = Age Int |
| 15 | + |
| 16 | +type Comment = Comment String |
| 17 | +type Description = Description String |
| 18 | +``` |
| 19 | + |
| 20 | +By wrapping the primitive values like this, the type system can now help you make sure that you never mix up a `Id` and an `Age`. Those are different types! This trick is extra cool because it has no runtime cost in `--optimize` mode. The compiler can just use an `Int` or `String` directly when you use that flag! |
| 21 | + |
| 22 | +The problem arises when you want to use a `Id` as a key in a dictionary. This is a totally reasonable thing to do, but the current version of Elm cannot handle this scenario. |
| 23 | + |
| 24 | +Instead of creating a `Dict Id Info` type, one thing you can do is create a custom data structure like this: |
| 25 | + |
| 26 | +```elm |
| 27 | +module User exposing (Id, Table, empty, get, add) |
| 28 | + |
| 29 | +import Dict exposing (Dict) |
| 30 | + |
| 31 | + |
| 32 | +-- USER |
| 33 | + |
| 34 | +type Id = Id Int |
| 35 | + |
| 36 | + |
| 37 | +-- TABLE |
| 38 | + |
| 39 | +type Table info = |
| 40 | + Table Int (Dict Int info) |
| 41 | + |
| 42 | +empty : Table info |
| 43 | +empty = |
| 44 | + Table 0 Dict.empty |
| 45 | + |
| 46 | +get : Id -> Table info -> Maybe info |
| 47 | +get (Id id) (Table _ dict) = |
| 48 | + Dict.get id dict |
| 49 | + |
| 50 | +add : info -> Table info -> (Table info, Id) |
| 51 | +add info (Table nextId dict) = |
| 52 | + ( Table (nextId + 1) (Dict.insert nextId info dict) |
| 53 | + , nextId |
| 54 | + ) |
| 55 | +``` |
| 56 | + |
| 57 | +There are a couple nice thing about this approach: |
| 58 | + |
| 59 | +1. The only way to get a new `User.Id` is to `add` information to a `User.Table`. |
| 60 | +2. All the operations on a `User.Table` are explicit. Does it make sense to remove users? To merge two tables together? Are there any special details to consider in those cases? This will always be captured explicitly in the interface of the `User` module. |
| 61 | +3. If you ever want to switch the internal representation from `Dict` to `Array` or something else, it is no problem. All the changes will be within the `User` module. |
| 62 | + |
| 63 | +So while this approach is not as convenient as using a `Dict` directly, it has some benefits of its own that can be helpful in some cases. |
| 64 | + |
| 65 | + |
| 66 | +## Enumerations to Ints |
| 67 | + |
| 68 | +Say you need to define a `trafficLightToInt` function: |
| 69 | + |
| 70 | +```elm |
| 71 | +type TrafficLight = Green | Yellow | Red |
| 72 | + |
| 73 | +trafficLightToInt : TrafficLight -> Int |
| 74 | +trafficLightToInt trafficLight = |
| 75 | + ??? |
| 76 | +``` |
| 77 | + |
| 78 | +We have heard that some people would prefer to use a dictionary for this sort of thing. That way you do not need to write the numbers yourself, they can be generated such that you never have a typo. |
| 79 | + |
| 80 | +I would recommend using a `case` expression though: |
| 81 | + |
| 82 | +```elm |
| 83 | +type TrafficLight = Green | Yellow | Red |
| 84 | + |
| 85 | +trafficLightToInt : TrafficLight -> Int |
| 86 | +trafficLightToInt trafficLight = |
| 87 | + case trafficLight of |
| 88 | + Green -> 1 |
| 89 | + Yellow -> 2 |
| 90 | + Red -> 3 |
| 91 | +``` |
| 92 | + |
| 93 | +This is really straight-forward while avoiding questions like “is `Green` less than or greater than `Red`?” |
| 94 | + |
| 95 | + |
| 96 | +## Something else? |
| 97 | + |
| 98 | +If you have some other situation, please tell us about it [here](https://github.com/elm/error-message-catalog/issues). That is a log of error messages that can be improved, and we can use the particulars of your scenario to add more advice on this page! |
0 commit comments