Route Prism

Deriving IsRoute for our route types gives us a routePrism function that returns (effectively 1 ) 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,

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_ $
      ( \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")

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.


In reality, routePrism must return a Prism_ type that Ema provides.

Prism_ is isomorphic to Prism' – with conversion functions toPrism_ and fromPrism_. The typeclass is obliged to use Prism_ instead of Prism' due to a Haskell limitation in regards to DerivingVia and coercions (see details here).

Links to this page
  • Upgrading
    Ema.routeUrl: change to accept the Route Prism that is now passed to siteOutput
  • Route type

    An Ema app is defined by its route type. All routes must be an instance of the IsRoute class, which provides a route encoder (see Route Prism) that is used to convert to and from the corresponding .html filepaths. This instance can be hand-written or derived generically.

  • Model type
    RouteModel of IsRoute associates a route type with a model type. This enables Route Prism (and routeUniverse) to accept that model value as an argument.
  • IsRoute
    routePrism gives us an optics prism (see Route Prism) that can be used to encode and decode between the route type and the .html filepath.

    The IsRoute typeclass is used to mainly define a Route Prism-based encoding for your route types. In other words, deriving IsRoute for the example route,

  • Add Routes
    routePrism: produce Route Prism (a Prism') that we can use to encode routes to URLs and vice versa.