Choice of template language for tyxml

Tyxml has a ppx which allows to type regular HTML and get tyxml elements out. I have been working on-and-off on a templating version which allows to type in templated HTML and get a statically typed function that returns tyxml elements. Additionally, a tool tyxml_template could transform a file foo.html into a function Foo.make that takes an argument for each hole in the template and returns a tyxml element. The goal here is not to superseed tyxml combinators but instead to let designers design without having to learn OCaml before importing their work into the typed OCaml world.

The first prototype used mustache. However, mustache is a limited language, and some people expressed the wish to have a something more powerful. My only constraint is that I prefer something standard, to avoid imposing a whole new template language on people.

So, I would like to spark a discussion and collect preferences of the wider community of (potential) tyxml users. So far, I see the following choices:

  • mustache
    Good: Simple. Standard. Very solid implementation thanks to recent work by @Armael.
    Bad: Simple.
  • handlebars.js
    Good: Standard. The web programming community seem to like it.
    Bad: No OCaml implementation yet. The language is very rich. Providing an implementation of sufficient quality would be a non-trivial effort.
  • Jinja templates with the OCaml implementation, Jingoo
    Good: Seem popular and close to Django templates. The OCaml implementation look solid.
    Bad: The language look very rich, although more reasonable than handlebars.
  • Something non-standard such as snabela.
    Good: Easier to adapt the template language to our need.
    Bad: Not standard.

Thoughts, propositions ?

4 Likes

I took another more in-depth look at jinja. The language is very big. Much bigger than I expected. It has assignments, macro definitions and calls and a whole expression language to go with the control structures. Transforming even part of that into a static OCaml function is a much bigger task than what I did for mustache. That being said, the semantics looks more reasonable than mustache/handlebars.

Just curious. What’s the kind of problem with these ?

It’s quite off-topic, but alright.

Mustache’s definition of scope is, imho, quite questionable. For example in {{#foo}}{{name}}{{/foo}}, the fact that name is a subfield of foo or not is completely dynamic and depends on the json being fed. There are lot’s of other less relevant small things (the handling of whitespace is slightly insane).
Another issue is that there is no real specification, just a set of examples that don’t even match the documentation. Furthermore, the language has seen no maintenance for several years, forcing everyone to provide slightly different extensions (exactly like markdown before the recent standardization effort).

Handlebars is more or less an extension of mustache and got the same questionable notion of scope, but the biggest issue for me is that the semantics of most elements is interpreted in term of Javascript functions (and the language can trivially be extended that way).

Jinja, on the other hand, introduce names explicitly with actual binders and seem to define the semantics of the language directly, without referring (much) to Python. As a consequence, it’s a very big language with full blown functions, inheritance, control structures, etc.

However, I’m not the core target for this (I just use OCaml combinators), hence the current topic, to figure out what other users prefer.

3 Likes

Having a more powerful templating engine seems like a very good idea! I will be happy to switch ramen to something saner and type-checked :slight_smile:

Mustache is simple but it has some weird semantics for blocks and some missing features (no if/then/else, no recursive templates).

Why is making a choice necessary here? You’re providing this template sub language for users who would like something that they’re more familiar with or is consistent with their existing codebase. In such a situation, either mustache, jinja, or something else could be the right choice. Yes, supporting multiple languages is obviously a hassle, but I assume that users of these respective languages will be able to add their own support. Perhaps it makes sense to make it easier for them to do that?

2 Likes

The ppx translates HTML into a nest of OCaml combinators with the occasional holes. Note that the representation of HTML here is not textual at all: it can use any of the tyxml backends. For example:

open Tyxml_js
let f x = [%html {|
  <p class=foo>This was never text: <span>|}x{|</span></p>
|}]
val f :
  [< Html_types.span_content_fun ] Tyxml_js.Html.elt list ->
  [> Html_types.p ] Tyxml_js.Html.elt

This directly builds a DOM tree, without ever going through the textual representation. It will also work with js_of_ocaml’s reactive nodes, eliom’s shared node, virtual_dom, and so on.

So, I need to reinterpret the constructs of the template language in terms of the primitives that allow me to build HTML trees. I can’t simply take the host interpreter and applies that. Furthermore, in order to make it all fit, I need to add some typing rules to the template language for it to make sense.

All this is very specific to OCaml, tyxml and each individual template language and the technologies they use. Maybe you can make it parametric, but I have no idea how that could work. Given the nature of this work, It’s something I’m willing to do once, but not more. :slight_smile:

One thing I think is important for a template language is to be able to compose templates, in the template language (so that you can define “components” as templates, and compose them to form bigger components that are ultimately filled by the application code).

For example, this is not really possible with Mustache: there is a feature of “template inclusion”, but it is just that, an include. This means the “free variables” (or parameters) of the included template just capture whatever is in the scope. Thus it is not possible to have a template that imports a child template and sets its parameters to some values. I guess some workarounds are possible, but everything I thought of felt highly non modular and inconvenient.

Note that I have little experience doing web development and that kind of stuff, so I’m curious of what people more versed in this field think about this.

I used twig when I was doing PHP using Symfony. It was quite good.

For the record: the recent work by @gasche adding “Hogan-style template inheritence” to ocaml-mustache basically solves the issue I was mentioning in my previous post.

2 Likes