lucas' webpage

Ondim Manual

software

Ondim is a library for creating templating engines for arbitrary formats, inspired by Heist (which only works for HTML). It includes HTML, JSON, Pandoc, LaTeX and Whiskers (a custom format similar to Mustache) definitions that work out-of-the-box.

The main idea is to reuse existing libraries that will provide the target language’s parser, types and renderer, and easily build an expansion layer on top. If you are familiar with Heist, for HTML this is done by binding named expansions (in Heist they are called splices) that programatically transform the contents of HTML elements whose tags match the name.

Why Ondim?

Advantages of having the templates interpreted in the target document’s own language include:

  • No need for special editor support; make use of all the existing tools.
  • Less prone to errors; In some cases, it is even impossible for the templates to result in invalid documents — you would get a parse error before.
  • Context-aware means it’s possible to automatically handle contextual rules specific to each format, like text escaping.
  • Easier to understand visually compared a mix two languages.
  • More modular: no need to write a parser or writer if there are already existing ones.

Examples

This is how you could define an if/else expansion with Ondim:

ifElse :: (Monad m, OndimNode t) => Bool -> Expansion m t
ifElse cond node = do
let els = children node
yes = filter ((Just "else" /=) . identify) els
no =
maybe [] children $
find ((Just "else" ==) . identify) els
if cond
then liftNodes yes
else liftNodes no

Now, suppose you have a variable isCat :: Bool and you want your templates to choose between two snippets depending on its value. Such HTML template could have type [HtmlNode], so that if you lift it into the Ondim monad binding ifElse isCat to the name if-cat,

liftNodes myTemplateContent
`binding` do
"if-cat" #* ifElse isCat

you can now write in your HTML templates…

<e:if-cat>
<p>I have a <i>beautiful</i> cat!</p>
<e:else>
<p>I don't have a cat 😭</p>
</e:else>
</e:if-cat>

…and the output will depend on the value of isCat.

Polymorphism

The beauty of Ondim (and one aspect that takes it further from Heist) is that the ifElse example above is polymorphic on the format type, so the same code works for formats other than HTML. In a Pandoc setting, the same code would work if myTemplateContent had type [Block], and the same template could be written as:

::: e:if-cat
I have a _beautiful_ cat!

:::: e:else
I don't have a cat :sob:
::::
:::

This is written in plain Pandoc markdown, and meant to be parsed by the Pandoc reader itself. Note that instead of HTML tags, now we annotate the expansions with fenced divs and bracketed spans.

Similarly, you could write the same thing in other formats like JSON or LaTeX. It is also considerably easy to add support to new languages, since due to the use of multiwalk’s generics, most of the boilerplate is simply a matter of instantiating generic instances.

Similar projects