Deriving IsRoute for our route types gives us a routePrism function that returns (effectively1) a Prism' FilePath r (from optics-core Prism') that in turn can be used to encode and decode route values to and from URLs or filepaths.
Let’s consider the example route,
-- An example of nested routes
-- Route's expected encoding is given as a comment.
data Route
= Route_Index -- index.html
| Route_About -- about.html
| Route_Contact -- contact.html
| Route_Blog BlogRoute -- blog/<BlogRoute>
data BlogRoute
= BlogRoute_Index -- index.html
| BlogRoute_Post Slug -- post/<Slug>
newtype Slug = Slug { unSlug :: String }
Here is a naive implementation of IsRoute for the BlogRoute above:
instance IsRoute BlogRoute where
type RouteModel BlogRoute = ()
routePrism () = toPrism_ $
prism'
( \case
BlogRoute_Index -> "index.html"
BlogRoute_Post slug -> "posts" </> slug <> ".html"
)
( \case
"index.html" -> Just BlogRoute_Index
(parsePostSlug -> Just slug) -> Just $ BlogRoute_Post slug
_ -> Nothing
)
routeUniverse () = []
In GHCi you can play with this prism as,
ghci> let rp = routePrism @BlogRoute ()
ghci> preview rp "posts/foo.html"
Just (BlogRoute_Post (Slug "foo"))
ghci> review rp $ BlogRoute_Post (Slug "foo")
"posts/foo.html"
Ema provides a routeUrl function that converts this filepath to an URL.
Generic prism
routePrism can also be generically determined for routes with “standard shapes” (both Route and BlogRoute above); see Generic deriving.