Nested ADT in Routing


#1

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.


#2

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


#3

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

I still don’t quite get why that works, but never mind.


#4

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)


#5

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 “<<”.


#6

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…


#7

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.


#8

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.


#9

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.


#10

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.


#11

Isn’t there a risk of losing track once there are lots of routes?


#12

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