Home

Awesome

Using HTML to learn ClojureScript

or why JSX does not have templating logic

There is actually a pretty interesting path from HTML templating to ClojureScript. So if you're interested in learning ClojureScript, read on!

We will first talk about frustrations with the current state of HTML-templating in the JS community. And we will look at an alternative that quickly became the status quo in the ClojureScript community.

Extending HTML for templating

The following is a simple HTML example:

<div>
 <img href="hi.jpg">
 <span>Hello</span>
</div>

We can inject dynamic values into HTML using a templating language like Handlebars.

<div>
 <img src="{{imageSource}}" />
 <span>{{message}}</span>
</div>

If we need conditional/generative logic:

<div>
 {{#if imageSource}}
   <img src="{{imageSource}}" />
 {{/if}}
 {{#each messages}}
   <span>{{.}}</span>
 {{/each}}
</div>

When template size and complexity grows, we start demanding more language features in our templates, like template chunks to allow a form of function composition.

We can continue adding custom syntax to our templates, but we may start feeling weird about inventing our own fully-capable language around HTML. Perhaps there is a better way.

Embedding HTML in JS

The React team from Facebook recognized the awkwardness of using a language on top of HTML for sufficiently complex templates, so they tried generating HTML inside of Javascript to benefit from a full language (among other reasons related to virtual DOM construction):

React.render(
  React.createElement("div", null,
    React.createElement("img", {src: imageSource}),
    React.createElement("span", null, message)));

For brevity, they added an optional sugar on top of JS called JSX, essentially allowing us to use HTML tags inside JS. (JS expressions are also allowed in the HTML tags by using curly braces.)

// this desugars into the previous code example
React.render(
  <div>
    <img src={imageSource} />
    <span>{message}</span>
  </div>);

We get function composition without any special syntax since we're in JS:

function myImage(imageSource) {
   return <img src={imageSource} />;
}

React.render(
  <div>
    {myImage(imageSource)}
    <span>{message}</span>
  </div>);

And we can use Javascript's ternary expressions for conditionals and the native map function for sequence generation:

React.render(
  <div>
    {imageSource ? <img src={imageSource} /> : null}
    {messages.map(m => <span>{m}</span>)}
  </div>);

Alternatively, we can use JSX Control Statements which overload the nature of a JSX tag:

React.render(
  <div>
    <If condition={imageSource}>
      <img src={imageSource} />
    </If>
    <For each="m" of={messages}>
      <span>{m}</span>
    </For>
  </div>);

So, we gained the full power of a real language by embedding HTML inside of Javascript. But we find ourselves again adding more syntax, this time for brevity. Is there a language that allows us to do this without adding a custom syntax?

Redefining our data

Let's go back to our simple HTML example:

<div>
  <img src="hi.jpg" />
  <span>Hello</span>
</div>

This is fundamentally just a tree of data, which we can represent with a nested bulleted list.



Trees are often represented as nested lists, so let's see it in JSON:

["div",
  ["img", {"src": "hi.jpg"}]
  ["span",
    "Hello"]
]

We assume the first element of a list is the tag name. Second element can be a map of tag attributes. Rest of the elements are child tags.

Can we add logic syntax to this somehow? Going the way of Javascript (e.g. ternary operator and map) suffers from the same problems we saw in the previous section-- a lack of brevity. And we're specificially trying to not invent new syntax.

Adding logic

It turns out there already exists a general purpose language that is like JSON, but with logic as well. Let's see an example with data first. Notice the lack of commas and colons:

["div"
  ["img" {"src" "hi.jpg"}]
  ["span"
    "Hello"]
]

To see the logic, let's add a conditional around our img:

["div"
  (if imageSource
    ["img" {"src" imageSource}]
  )
  ["span"
    "Hello"]
]

Notice we have inserted the if conditional as a list with parens. You can probably guess what it does, but you might still be confused. Why is the if logic represented as a list?

This is actually very similar to what Handlebars and JSX Control Statements are doing; they represent logic as data. For example, to represent conditionals, Handlebars has the {{#if}} {{/if}} tags, and JSX Control Statements has the <If> </If> tags. And we already saw how a tag is simply a list. Likewise, we're using an if list to represent a conditional.

The only difference is that we didn't have to invent the if list syntax. It was actually here all along in the language, with brevity and all.

ClojureScript

This language is called Clojure, and its JavaScript target is called ClojureScript. Its syntax is a marriage of Lisp and JSON (sort of). All paren lists are interpreted as code, with the first argument being the function name, and the rest as arguments. Let's complete our example with a for loop (simplified):

["div"
  (if imageSource
    ["img" {"src" imageSource}]
  )
  (for m messages
    ["span" m]
  )
]

And if you're wondering how to turn this into an HTML markup string or a React component tree, there are different html functions for doing that. Here's how you'd use it. Remember, a paren list is just a function call:

(html
  ["div"
    (if imageSource
      ["img" {"src" imageSource}]
    )
    (for m messages
      ["span" m]
    )
  ]
)

Clojure is not built specifically for HTML-templating, of course. Rather, it is a general-purpose language that embraces the fundamental nature of code as evaluated data, and simple HTML-templating just falls out of that idea.

The popular templating style we just described is known as Hiccup and is the status quo for templating in ClojureScript.

In Practice

We left this out for simplicity, but keywords are used instead of strings for the tag names and attributes (e.g. :div, :img):

[:div
  [:img {:src "hi.jpg"}]
  [:span "Hello"]]

And this convention is to never leave closing delimiters on their own line:

;; conventional ClojureScript formatting
(html
  [:div
    (if imageSource
      [:img {:src imageSource}])
    (for [m messages]
      [:span m])])

It's also worth mentioning that Hoplon uses a different syntax with all elements as idiomatic functions. [:div ...] -> (div ...)

(div
  (if imageSource
    (img :src imageSource))
  (for [m messages]
    (span m)))

Whatever way you represent the elements in ClojureScript, its notions are clear and consistent with the rest of the language. Syntax interpretation is a matter of knowing how a function will interpret its arguments, which can only take the shape of literal data.

But maybe too many parentheses?

Why not indent? (e.g. Haml, Slim, Jade)

Nobody likes large amounts of unimportant stuff lying around in code, like parentheses or closing tags. So it's no wonder that other popular templating languages have eliminated these redundancies through enforced indentation, and optional delimiting. Afterall, indentation is the natural way that we represent nested lists (as we showed before):

Deducing structure through indentation makes sense, but we must also deduce the types of elements to prevent ambiguity:

Let's see how the popular indentation-based templating languages deal with ambiguity of element types:


Haml is used in the Ruby community:

-# Haml
%div
  - if imageSource
    %img{:src => imageSource}
  - messages.each do |m|
    %span= m

Slim is also used in the Ruby community, and perhaps looks simpler:

/ Slim
div
  - if imageSource
    img src=imageSource
  - messages.each do |m|
    span = m

Jade is a dominant version in the JS community:

// Jade
div
  if imageSource
    img(src=imageSource)
  each m in messages
    span= m

Each language has its own syntax for dealing with how elements are evaluated.

TODO: conclusions by comparing syntax of evaluation semantics, and compare to general purpose semantics of cljs

If we wish to get the benefits of a general purpose language (with consistent evaluation semantics) and the conciseness of indentation, we can use ClojureScript with an upcoming editor feature called parinfer, which uses indentation to infer structure, and shows proper delimiters without sacrificing readability for ambiguity.


Appendix

ClojureScript Syntax in 15 Minutes

To learn more about the syntax, you can check out ClojureScript Syntax in 15 minutes.

Hiccup-style templating

The popular templating pattern used in Clojure is called "Hiccup"-style. You can use it through the following libraries: