Nested ADT in Routing

I have an application using Navigation and UrlParser which will have perhaps 200 Routes. Rather than place all the routes in a single file, it would be useful to be able to keep them in other modules, close to where the page rendering takes place.

So, I need to used nested ADTs in my routing.

In the simplest case, this presents no problem.

type OtherAnswer = DontKnow | Perhaps | NoComment

type Route = Yes | No | Other OtherAnswer

route : Url.Parser (Route -> a) a
route =
  Url.oneOf
    [ Url.map Yes (s "Yes")
    , Url.map No (s "")
    , Url.map (Other DontKnow) (s "DontKnow")
    , Url.map (Other Perhaps) (s "Perhaps")
    , Url.map (Other NoComment) (s "NoComment")
   ]

No problem. But one of the nested ADTs now has to carry a payload, thus:

type OtherAnswer = DontKnow | Perhaps | NoComment String

So, how should the ā€˜routeā€™ function look?

route : Url.Parser (Route -> a) a
route =
  Url.oneOf
    [ Url.map Yes (s "Yes")
    , Url.map No (s "")
    , Url.map (Other DontKnow) (s "DontKnow")
    , Url.map (Other Perhaps) (s "Perhaps")
    , Url.map (Other NoComment) (s "NoComment" </> string)
]

Nope, the last line doesnā€™t work, because one of the items in Other is ā€˜NoComment Stringā€™, and I am not offering that. Iā€™ve tried everything I can think of, but Iā€™m always an argument short or an argument over.

Iā€™m missing something simple, no doubt, but I would appreciate any help.

This one should be Url.map (Other << NoComment) (s "NoComment" </> string).
https://ellie-app.com/CfMZZmVqKCa1

Thank you, once again, for your prompt and helpful assistance.

I still donā€™t quite get why that works, but never mind.

NoComment tag behaves like String -> OtherAnswer

Other behaves as OtherAnswer -> Route

Other << NoComment behaves as the composition of those two String -> Route

Iā€™m curious, would this have been better?
Url.map (\str -> Other (NoComment str)) (s "NoComment" </> string)

Depends on your definition of ā€˜betterā€™, but it definitely is easier to grasp for newcomers to the language :smiley:.

People first have to learn that fun1 << fun2 means \x -> fun1 (fun2 x)), because it is not immediately obvious from the characters ā€œ<<ā€.

1 Like

The function composition version looks cleaner to me.

I need to sit down and work through the type signatures of UrlParser to understand exactly whatā€™s going onā€¦

You can look at the code of the library, it is only 146 lines of code but donā€™t worry if it is not clear right away. It is by far one of the most difficult things I have encountered so far in Elm.

Yes, the type signatures take some getting used to.

But Iā€™ve been working on it today, and have managed to move all module-specific routing code into the relevant modules,including the Url.oneOf and routeToString pieces.

So it all looks much cleaner now.

Have you seen elm-spa-example? I personally find it very helpful to have the routes and all the route operation in their Route module.

Yes, I have seen it. Itā€™s certainly easier that way.

I guess itā€™s a question of taste ā€“ I like to keep the different operations buttoned up in their separate modules.

Isnā€™t there a risk of losing track once there are lots of routes?

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