# Typesafe Url.Parser.oneOf usage?

**URL:** <https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964>\
**Category:** Learn\
**Created:** [September 14, 2018, 8:03pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964 "2018-09-14T20:03:01Z")\
**Posts on this page:** 12\
**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:** [September 14, 2018, 8:03pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/1 "2018-09-14T20:03:01Z")

</div>

Is there a way to make sure I don’t forget to add a parser for a new Route when I extend the type with a new constructor?

This is the best I could come up with, which is not very typesafe:

```auto
parser : Parser (Route -> a) a
parser =
    Url.Parser.oneOf
        [ handlers.signup.urlParser
        , handlers.login.urlParser
        , handlers.refresh.urlParser
        , handlers.logout.urlParser
        , handlers.refreshAnonymous.urlParser
        , handlers.attack.urlParser
        ]

{-| 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"

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

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

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

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

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

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

```

---

<div class="post-metadata">

**Author:** ![progger](https://avatars.discourse-cdn.com/v4/letter/p/5daacb/32.png) [@progger](https://discourse.elm-lang.org/u/progger)\
**Post date:** [September 14, 2018, 8:51pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/2 "2018-09-14T20:51:53Z")

</div>

How about make a function getParser : Route -\> Parser a.

```auto
parser =
   Url.Parser.oneOf (List.map getParser allRoutes)

-- this ensures that a parser exists for the type.
getParser : Route -> Parser a
getParser route =
   case route of
       NotFound ->
           handler.notfound.urlparser

       Signup auth ->
           handler.signup.urlparser

       Refresh ->
           handler.refresh.urlparser

       RefreshAnonymous ->
           handler.refreshanonymous.urlparser

       Login auth ->
           handler.login.urlparser

       Attack attackData ->
           handler.attack.urlparser

       Logout ->
           handler.logout.urlparser

-- you still have to remember to put your type here!
allRoutes =
   [ NotFound
   , Signup
   , Refresh
   , RefreshAnonymous
   , Login
   , Attack
   , Logout
   ]

```

---

<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:** [September 14, 2018, 9:37pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/3 "2018-09-14T21:37:42Z")

</div>

Right, but it’s a bit unwieldy if your routes take parameters. And it’s still no better as far as “having to remember to do something” goes…

---

<div class="post-metadata">

**Author:** ![progger](https://avatars.discourse-cdn.com/v4/letter/p/5daacb/32.png) [@progger](https://discourse.elm-lang.org/u/progger)\
**Post date:** [September 17, 2018, 2:21pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/4 "2018-09-17T14:21:33Z")

</div>

I had a sudden thought on this! Ok you’d still have to use fake args for your types that require them, and there’s an additional function to maintain. But you get an automatically generated list of all the types!

```auto

nextType : Route -> Maybe Route
nextType : route = 
  case route of 
    NotFound -> Just Signup
    Signup -> Just Refresh
    Refresh -> Just RefreshAnonymous
    RefreshAnonymous -> Just Login
    Login -> Just Attack
    Attack -> Just Logout
    Logout -> Nothing

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

allRoutes : List Routes
allRoutes = makeAllRoutes NotFound

```

---

<div class="post-metadata">

**Author:** ![Warry](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/warry/32/3998_2.png) [@Warry](https://discourse.elm-lang.org/u/Warry)\
**Post date:** [September 17, 2018, 3:02pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/5 "2018-09-17T15:02:56Z")

</div>

It’d be nice if the compiler were able to detect missing branches in (opted-in?) oneOf s. It could benefit Routing, Json, or all kinds of parsing/data building… Not sure it’s feasible (because order can be critical, some are values others are functions…), but it’d be a big step toward ensuring all possible states are covered.

---

<div class="post-metadata">

**Author:** ![enetsee](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/enetsee/32/869_2.png) [@enetsee](https://discourse.elm-lang.org/u/enetsee)\
**Post date:** [September 17, 2018, 4:26pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/6 "2018-09-17T16:26:27Z")

</div>

I don’t think this is possible to check statically with Elm’s type system.

This would be possible with polymorphic variants which are kind of like extensible rows for union (or variant, or ‘custom’ types).

Using polymorphic variants, if you have some function which has return type:

`[< 'Route1 | 'Route2 | 'Route3]`

And you attempt to use it with some regular union type which does not have these tags or less, it would be a type error.

So both of these wold be ok:

`type Routes = Route1 | Route2`

`type Routes = Route1 | Route2 | Route3`

but this would fail

`type Routes = Route1 | Route2 | Route3 | Route4`

---

<div class="post-metadata">

**Author:** ![itsgreggreg](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/itsgreggreg/32/462_2.png) [@itsgreggreg](https://discourse.elm-lang.org/u/itsgreggreg)\
**Post date:** [September 17, 2018, 4:44pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/7 "2018-09-17T16:44:15Z")

</div>

I think the general case here would be something like, “How do I have the compiler guarantee that every constructor of a Type is included in an enumerable?”

I don’t believe that is possible in elm, thus I don’t believe there is any way to guarantee what you are asking for without creating the enumerable yourself, but at that point you’ve not much gain over the standard way of route parsing.

---

<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:** [September 17, 2018, 4:48pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/8 "2018-09-17T16:48:01Z")

</div>

Thank you, this worked! Yeah it’s a bit more work, but it uses `case ... of` in the process and thus reminds me to plug the new constructor in “the chain”.

> <https://github.com/Janiczek/nu-ashworld/commit/5014d367caac48803268b0fea79b00c166018faa>

---

<div class="post-metadata">

**Author:** ![Qqwy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/qqwy/32/1062_2.png) [@Qqwy](https://discourse.elm-lang.org/u/Qqwy)\
**Post date:** [September 17, 2018, 5:53pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/9 "2018-09-17T17:53:54Z")

</div>

Related to this discussion is [How to represent that a set of data is complete in a type-safe manner](https://discourse.elm-lang.org/t/how-to-represent-that-a-set-of-data-is-complete-in-a-type-safe-manner/1967), which, unfortunately, also did not have a really nice conclusion (at least at the time of writing).

Ultimately what is another hurdle is that it is rather common to represent a Route multiple times (like e.g. common misspellings, have the singular word redirect to the plural word, or allow route-names in multiple languages), so that’s one pattern that you cannot use at the same time as using the approach @progger proposes.

---

<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:** [September 17, 2018, 6:11pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/10 "2018-09-17T18:11:35Z")

</div>

Oh, good point! Yes, that would be a problem. In that regard the approach from my original question is probably more flexible.

---

<div class="post-metadata">

**Author:** ![Qqwy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/qqwy/32/1062_2.png) [@Qqwy](https://discourse.elm-lang.org/u/Qqwy)\
**Post date:** [September 17, 2018, 6:30pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/11 "2018-09-17T18:30:03Z")

</div>

By the way, is there a way in ‘debug’ Elm to get a list of all the different data constructors? Testing an URL parser is the kind of thing that might be worthwhile (since we lack type-level lists) to do using QuickCheck/Fuzzing… as long as those tests can of course be generated automatically based on the actual type that is constructed. (Because of course, having to manually maintain an URL fuzzer on top of the URL parser and the URL builder is not useful).

There are a lot of interesting blog posts talking about how to do URL parsing (and/or building) in Haskell, but most of these realize that some type-level trickery really is useful here:

- [https://www.reddit.com/r/haskell/comments/3u8psl/the\_overflow\_haskell\_and\_typesafe\_routing/](https://www.reddit.com/r/haskell/comments/3u8psl/the_overflow_haskell_and_typesafe_routing/)
- [https://www.spock.li/2015/04/19/type-safe\_routing.html](https://www.spock.li/2015/04/19/type-safe_routing.html)
- [https://ocharles.org.uk/blog/posts/2013-12-20-24-days-of-hackage-web-routes-boomerang.html](https://ocharles.org.uk/blog/posts/2013-12-20-24-days-of-hackage-web-routes-boomerang.html)

---

<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:** [September 27, 2018, 6:30pm UTC](https://discourse.elm-lang.org/t/typesafe-url-parser-oneof-usage/1964/12 "2018-09-27T18:30:07Z")

</div>

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