# Eighty

Simple and fast HTML generation

Posted on March 16, 2018

HTML templating systems are great but they sure are complex. ASP.NET’s Razor, for example, is a whole new programming language! While Razor does happen to have a large chunk of C# embedded within it, and it works by generating and then compiling C# code, it’s still a separate language with a separate syntax, separate abstraction techniques, separate compiler tooling, a separate file type, and separate (and usually inferior) editor support. All this for a task as simple and common as generating HTML!

This overhead can be worth it if you’re building a complex web application, but for simple tools such as report generators or email batch mailers Razor is unwieldy. Many people in these situations resort to generating their own HTML, either by building strings manually or by imperatively building tags using .NET’s supplied XML manipulation APIs. But there’s a whole world of possible designs out there, and there’s a lot of space in between “complex templating language” and “build strings by hand”.

## Eighty

Eighty (as in eigh-ty-M-L) is my attempt at striking a balance between these two extremes: not so abstract as to constitute a separate programming language, but not so concrete that you have to manipulate XML tags or strings manually. It’s a simple embedded domain-specific language which piggybacks on C#’s syntax, enabling you to write code resembling the HTML you’re generating. Rather than embedding C# into an HTML generator, Eighty embeds an HTML generator into C#.

Here’s an example from the readme:

var html = article(@class: "readme")._(
h1(id: "Eighty")._("Eighty"),
p_(
"Eighty (as in ",
i_("eigh-ty-M-L"),
") is a simple HTML generation library."
)
);

Eighty is organised around the Html class, being an immutable chunk of HTML which knows how to render itself using its Write(TextWriter) method. Html defines a large collection of static methods (designed to be imported with using static), with names like h1 and p, which create Html values representing their respective tags, with a collection of children which are smaller Html values.

Eighty adopts some simple conventions for its HTML-esque domain-specific language:

• Tags are created using (lower-case) methods like p() and i().
• Attributes are passed as optional named arguments: a(href: "benjamin.pizza", @class: "website-link"). I can’t force you to name your arguments — you could pass them positionally — but that’s not a good idea.
• A tag’s children are introduced using the _ character, which can appear at the end of a method name or as a method name all by itself. a(href: "benjamin.pizza")._("Visit my website") creates an a tag with an href attribute and some text inside it; p_("a paragraph of text") represents a p tag with some text but no attributes. I chose _ because it’s the least noisy character that can be used as an identifier in C#.
• Strings can be implicitly converted to Html and are interpreted as HTML text. Text is HTML-encoded by default. You can opt out of this using the Raw method.

## Eighty vs Razor

Of course, C# code will only ever look a bit like HTML. Razor code looks much more like HTML than this! This can be a drawback when you’re working with designers who want to read and write HTML — I’m planning to write a tool to convert HTML text into an Eighty expression to partially ease this pain point. But Eighty has two big advantages which make it simpler and easier than Razor to program with:

1. It plugs into your existing system. You don’t require any extra tools to work with Eighty: if you can compile C#, you can use Eighty.
2. Programming with Eighty is just programming. Html instances are plain old immutable CLR objects, so you can use all your favourite techniques for abstraction and code reuse.

To illustrate the second point, here are some examples of how you might emulate some of Razor’s programming constructs using Eighty. In many of these cases Eighty does a better job than Razor of allowing abstraction and code reuse, because Eighty is embedded within C# rather than layered on top of C#.

### Models

In Razor, each view file you write declares a model type — the type of object it expects you to pass in to direct the generation of HTML. You use the @model directive at the top of your file, and then you can access members of the model in your Razor code.

@model ExampleModel

<h1>@Model.Title</h1>

One important disadvantage of Razor’s @model construct is that it is dynamically checked. The controller’s View method takes an object for the model parameter. You get a runtime error, without any feedback from the compiler, if you pass in a model whose type doesn’t match the view’s expected model type.

Since Eighty is embedded within C#, there’s no special syntax to declare the type of data a function depends on. You can just use a plain old parameter.

Html Example(ExampleModel model)
=> h1_(model.Title);

Since a template is a regular C# method, it’s much easier to run in a unit test harness than Razor. You can just call the method and make assertions about the generated HTML, either by looking at the string directly or by parsing it and traversing the resultant DOM.

Eighty includes an IHtmlRenderer<TModel> interface, which captures this pattern of parameterising a chunk of HTML by a model, but its use is optional — it’s used primarily by Eighty’s ASP.NET integration packages.

### Control flow

Razor allows you to mix markup with C#’s control flow constructs such as foreach and if. Here’s a simple example of populating a ul based on a list of values:

<ul>
@foreach (var item in Model.Items)
{
if (item.Visible)
{
<li>@item.Value</li>
}
}
</ul>

With Eighty, it’s a question of building different Html values. You can use LINQ’s high-level functional looping constructs:

return ul_(
model.Items
.Where(item => item.Visible)
.Select(item => li_(item.Value))
);

Or you can write your own loop and build a list:

var lis = new List<Html>();
foreach (var item in model.Items)
{
if (item.Visible)
{
}
}
return ul_(lis);

Mixing markup with C# is not a problem, because markup is C#.

### Partials and Helpers

Razor’s two main tools for code reuse are partial views and helpers. For the purposes of this article, they’re roughly equivalent. Partial views can be returned directly from a controller but their model type is checked at runtime, whereas helpers’ parameters are checked by the compiler but they can only be invoked from within a Razor view.

Eighty handles both of these uses in the simplest of ways: calling a function. If I want to include an HTML snippet in more than one place, I can just extract it into a method returning an Html object. Transliterating an example from the MVC documentation:

Html MakeNote(string content)
=> div(@class: "note")._(
p_(
strong_("Note"),
Raw("&nbsp;&nbsp; "),
content
)
);

Html SomeHtmlContainingANote()
=> article_(
p_("This is some opening paragraph text"),
MakeNote("My test note content"),
p_("This is some following text")
);

This is the best of both worlds: types are checked by the compiler as usual, but the returned Html value is a perfectly good standalone chunk of HTML, and can be rendered separately if necessary.

Html values being ordinary C# values, Eighty actually supports more types of reuse than Razor does. For example, you can pass a chunk of HTML as an argument, which is not easy to do with Razor:

Html RepeatFiveTimes(Html html)
=> _(Enumerable.Repeat(html, 5));

Since Html values are immutable, you can safely share them between different HTML documents, across different threads, etc. Sharing parts of your HTML document that don’t change can be an important optimisation.

### Layouts

Razor lets you define a shared layout page, which acts as a template for the other pages in your application. For example, you might put the html and body tags in a layout page, and use the built in RenderBody helper to render the concrete page’s body inside the body tag. This is also where global navs and the like are defined.

One way to handle global layouts and sections in Eighty would be to define an abstract base class. Each section becomes an abstract method, allowing individual pages to fill in their own HTML for those sections.

abstract class Layout
{
public Html GetHtml()
=> doctypeHtml_(
rel: "stylesheet",
type: "text/css",
href: "default.css"
),
Css(),
script(
type: "text/javascript",
src: "jquery-3.3.1.min.js"
),
Js()
),
body(
Body()
)
);

protected abstract Html Css();
protected abstract Html Js();
protected abstract Html Body();
}

Then, inheriting a layout is as easy as inheriting a class.

class DashboardPage : Layout
{
private DashboardModel _model;

public Dashboard(DashboardModel model)
{
_model = model;
}

protected override Html Css()
=> /* Dashboard-specific CSS */;

protected override Html Js()
=> /* Dashboard-specific scripts */;

protected override Html Body()
=> /* The body of the dashboard page */;
}

## Twenty

Eighty comes bundled with a second HTML generation library called Twenty. Twenty is harder to use correctly than Eighty, and its API is more verbose, but it’s faster.

HTML tags have to be balanced: every opening tag has to have a matching closing tag and vice versa. While an Html value is being written to a TextWriter, Eighty manages the stack of currently-open tags using the call stack. Each tag writes its opening tag, tells its children to write themselves, and then writes its closing tag. This is possible because Html is an ordinary reference type; the objects you build with methods like p() and h1() are tree-shaped objects representing a DOM of statically-unknown size.

Twenty instead takes an imperative view of HTML generation. Each tag method writes an opening tag to the TextWriter immediately, and returns an IDisposable which writes out the closing tag when it’s disposed. You, the programmer, use C#’s using statement to ensure that the Dispose method is called as soon as the children have been written. The structure of your HTML document is still visible in the code, but it’s present in the nesting of using statements, rather than by the structure of a tree-shaped object.

class MyHtmlBuilder : HtmlBuilder
{
protected override void Build()
{
{
using (h1(id: "Eighty"))
Text("Eighty");
using (p())
{
Text("Eighty (as in ");
using (i())
Text("eigh-ty-M-L");
Text(") is a simple HTML generation library.");
}
}
}
}

Perhaps this is a bit of an abuse of IDisposable, and the using syntax is comparatively noisy, but this trick allows Twenty to operate quickly and without generating any garbage while still making for a reasonable DSL. Compared to Eighty, Twenty does lose out on some flexibility and safety:

• You mustn’t forget a using statement, or call Dispose more than once, or Twenty will output malformed HTML. Eighty, on the other hand, will never generate bad HTML (notwithstanding the use of Raw).
• There’s no Html object — you can’t pass around chunks of HTML as first class values. This makes code reuse and abstraction somewhat more difficult.
• HtmlBuilder is not re-entrant. You can’t use the same HtmlBuilder from multiple threads.
• There’s no async API, because there’s no way to call Dispose asynchronously.

Given Twenty’s limitations, my advice is to write your markup using Html, and convert it to HtmlBuilder if you see that building Html values is a performance bottleneck.

## Performance

Eighty is pretty fast. I wrote a benchmark testing how long it takes to spit out around 30kB of HTML (with some encoding characters thrown in for good measure) while running in an in-memory hosted MVC application. Eighty’s synchronous code path does this around three times faster than Razor, and Twenty runs about 30% faster than that — so, four times faster than Razor.

What have I done to make Eighty fast? Honestly, not a huge amount. There are a only few interesting optimisations in Eighty’s codebase.

• Each call to TextWriter’s Write method is comparatively expensive, so rather than write individual snippets of HTML into the TextWriter directly, Eighty builds up a 4kB buffer and empties it out into the TextWriter when it fills up. The code to fill this buffer is a little fiddly, because you don’t know how long your input string is going to be after HTML-encoding it, so the HTML encoder has to write the encoded HTML in chunks. I toyed with a hand-written encoder, but I wanted to interoperate with ASP.NET’s pluggable HtmlEncoder, so I ended up calling that class’s low-level API.
• The buffer is managed by a mutable struct which is stored on the stack and passed by reference because mutable structs must never be copied. However, the async version cannot be a struct because async methods copy their this variable into a field behind the scenes. My first version of the code used the same mutable struct for both paths, which caused me some head-scratching when the async version didn’t work!
• There’s a fun and dangerous hack in Twenty’s codebase to allow storing a reference to one of these stack-allocated structs in a field. This is safe as long as the reference in the field doesn’t live longer than the stack location to which it refers, but you don’t get any compile-time feedback about this (I just have to program carefully and hope I don’t make a mistake). This hack makes critical use of C# 7’s “ref return types”, so it wouldn’t have been possible a couple of years ago.
• Calling an async method is comparatively expensive, even if it never goes async, because of the way async methods are translated by the compiler into code which builds and then executes a state machine. In the case of Eighty’s frequently-called WriteRawImpl method, it’s predictable whether a call will complete synchronously (that is, without calling the underlying TextWriter’s WriteAsync method). I split the async method into two parts — a fast wrapper which synchronously returns a Task and an async method which is only called when necessary — and got a ~15% speedup in my end-to-end benchmarks.
• Html values make use of ImmutableArrays to store their children. ImmutableArray is a thin wrapper over a regular array, so if you have a T[] you should be able to turn it into an ImmutableArray in-place without copying the contents, as long as you’re careful never to modify the original array after freezing it. There are several places in Eighty where this is a safe optimisation, but ImmutableArray doesn’t have a public API to do this. However, since ImmutableArray<T> is a struct with a single private T[] field, its runtime representation is the same as T[]’s. This makes it possible to unsafely coerce a T[] to an ImmutableArray<T> with no runtime cost.

I’m not sure exactly why Razor is slower by comparison. My guess is that Razor’s template compiler just tends to generate comparatively slow C# code — so there’s probably some room for improvement — but I would like to investigate this more.

HTML generators are an example of a problem where the spectrum of possible solutions is very broad indeed. Just within the C# ecosystem there exists a menagerie of different templating languages, as well as imperative object-oriented APIs like TagBuilder and streaming APIs like XmlWriter. Even Eighty and Twenty, two implementations of the same idea, are substantially different. You can often find yourself somewhere quite interesting if you appreach a common problem from a different direction than the established solutions. What parts of the library ecosystem do you think you could do with a fresh perspective?

Eighty is available on Nuget, along with some helpers to integrate Eighty with MVC and ASP.NET Core. API docs are hosted on this very domain, and the code’s all on GitHub where contributions and bug reports are very welcome!