Duplex routing with two sum types

Hey all,

I have a question about the routing-duplex library that is so well described here. I am trying to combine two sum types [sum, print, parse, /, Generic, root, noArgs are all imported from Routing.Duplex...]:

data Language = French | English

languageCodec :: RouteDuplex' Language
languageCodec =
    sum
        { "French": "fr" / noArgs
        , "English": "en" / noArgs
        }

derive instance genericLanguage :: Generic Language _

derive instance eqLanguage :: Eq Language

derive instance ordLanguage :: Ord Language

data Page
  = Main
  | Contact
  | About

derive instance genericPage :: Generic Page _

derive instance eqPage :: Eq Page

derive instance ordPage :: Ord Page

pageCodec :: RouteDuplex' Page 
pageCodec =
    sum
        { "Main": noArgs
        , "Contact": "contact" / noArgs
        , "About": "about" / noArgs
        }

I can do:

> print languageCodec <$> parse languageCodec "fr"
(Right "fr")

and

> print pageCodec <$> parse pageCodec "contact"
(Right "contact")

also

> print (root pageCodec) <$> parse (root pageCodec) "/contact"
(Right "/contact")

works well. But I cannot find out how to combine the two:

> print (languageCodec / pageCodec) <$> parse (languageCodec / pageCodec) "en/contact"
(Left (ExpectedEndOfPath "contact"))

This seems to parse the en part fine but then somehow expects end of path. Only this one works:

> print (languageCodec / pageCodec) <$> parse (languageCodec / pageCodec) "en"
(Right "en")

Any idea what the problem is here and how to do it better?

See https://github.com/natefaubion/purescript-routing-duplex/issues/15

I’m open to changing the current behavior.

Thanks for the answer. I read through the GitHub issue and finally understood that really the use case of sum is not what I am using it for. A simple two-way mapping from String to a custom type should be enough. I was able to work around it with as:

data Route
  = Route (Maybe Language) (Maybe Page)

derive instance genericRoute :: Generic Route _

derive instance eqRoute :: Eq Route

routeCodec :: RouteDuplex' Route
routeCodec =
  root
    $ sum
        { "Route": optional (languageCodec segment) / optional (pageCodec segment)}

data Language = French | English

derive instance eqLanguage :: Eq Language

languageCodec :: RouteDuplex' String -> RouteDuplex' Language
languageCodec = RD.as languageToRouteString routeStringToLanguage
    where
      languageToRouteString :: Language -> String
      languageToRouteString = case _ of
              French -> "fr"
              English -> "en"
      
      routeStringToLanguage :: String -> Either String Language
      routeStringToLanguage = case _ of
              "fr" -> Right French
              "en" -> Right English
              _ -> Right English

data Page
  = Main | Contact | About

derive instance eqPage :: Eq Page

pageCodec :: RouteDuplex' String -> RouteDuplex' Page
pageCodec = RD.as pageToRouteString routeStringToPage
    where
      pageToRouteString :: Page -> String
      pageToRouteString = case _ of
              Main -> "index"
              Contact -> "contact"
              About -> "about"
      
      routeStringToPage :: String -> Either String Page
      routeStringToPage = case _ of
              "index" -> Right Main
              "contact" -> Right Contact
              "about" -> Right About
              _ -> Right Main

It works. I added optional and a default case to the combined parser to reroute wrong routes to Main - not sure if that’s a good idea. Happy about suggestions how to improve this.