Hello World

Writing an Ema apps involves two things at a minimum:

  • a Route type type corresponding to the generated HTML file(s), as well as
  • an EmaSite instance on that route type defining the site render pipeline.

The simplest Ema app is presented below:

import Ema

-- The Route type of our site
newtype Route = Route ()
  deriving newtype
    (Show, Eq, Ord, Generic, IsRoute)

-- Site pipeline (input & output)
instance EmaSite Route where
  siteInput _ _ =
    -- There is no input in a hello-world site
    pure $ pure ()
  siteOutput _ _ _ =
    -- The output of index.html is simply a hello-world message in HTML
    pure $ Ema.AssetGenerated Ema.Html "<b>Hello</b>, Ema"

main :: IO ()
main = 
  -- Hook everything up in main using runSite.
  void $ Ema.runSite @Route ()

Let’s walk through this code:

  • The Route type represents the pages on our site. As there is only one page (index.html) in our hello-world site, we simply use ().
    • The unit type, (), already has an IsRoute instance, so we derive it via newtype.
    • In Add Routes, you will see how to write more elaborate route types and derive IsRoute for them. IsRoute is what tells Ema that a Haskell type is a route type (with URL encoders and decoders).
  • The EmaSite typeclass defines the “site pipeline” – the input Model type and the output Asset:
    • siteOutput renders this route.
    • siteInput returns the model used in rendering the routes. In Add a Model we will use a custom model, and in Dynamic Model we will make it time-varying.
    • Ema.runSite takes a route type (via TypeApplications), and runs the Ema site.
  • Running the resultant executable without arguments runs the Live Server, whereas running it with the gen subcommand will generate the static site (see CLI).

Next, we will explain how to write a simple mood tracker in Ema.