In the previous post we’ve managed to create a page with links which are typechecked. I believe that’s really cool, but we’ve missed one ultra-important feature: error 404: Not Found. Current implementation (available with step-by-step changes all the time at GitHub) returns a default page in this case – with <<Unknown>> project. That’s an ugly workaroud for the fact that we didn’t know how to return errors back then. Now we’re going to learn how to do it.

Let’s start with splitting getProjectR (in Project.hs) into three separate functions, like that:

getProjectR :: Int -> Handler Html
getProjectR projectId = defaultLayout $ selectPage projectId

selectPage :: Int -> Widget
selectPage projectId = renderPage project where
  project = maybe (-1, ""::Text) id projectEntry
  projectEntry = find (\x -> (fst x) == projectId) projects

renderPage :: (Int, Text) -> Widget
renderPage projectEntry = do
  setTitle $ ("Project " ++ projectName)
  $(widgetFile "project")
  where
    projectTitle = (toHtml projectId) ++ (toHtml projectName)
    projectName = snd projectEntry
    projectId = fst projectEntry

I’ve added type signatures to top-level functions, both to make GHC stop complaining and to introduce another important topic which we’ll discuss later in the series – Widgets. For now let’s simply assume that Widget is part of a web page.

Now, since selectPage is the only entity aware that something might go wrong, we should change it to return a 404 page in such case. It actually turns out to even remove some code:

selectPage :: Int -> Widget
selectPage projectId = maybe notFound renderPage projectEntry
  where
    projectEntry = find (\x -> (fst x) == projectId) projects

Check it out – it really does the job!
When I started writing this post I thought about implementing it the brute force way (fromJust). However, classy-prelude, a modified version of standard library which Yesod relies on doesn’t have a fromJust function. That turned out being actually quite nice, as I believe current implementation to be quite elegant, while the brute-force one would by, by definition, a brute-force one.

Next episode – more about Widgets – coming still this week. By the way, sorry about the delay in adding this post, I wanted to add it during the weekend but was unable to. I’ll do my best to avoid such situations in the future.