I’ve been playing with EEx, and I’ve noticed it doesn’t seem to optimize the templates during compilation. For example:
iex(32)> EEx.compile_string("ab")
{:<>, [context: EEx.Engine, import: Kernel], ["", "ab"]}
iex(33)> EEx.compile_string("a<%\n%>b")
{:<>, [context: EEx.Engine, import: Kernel],
[{:__block__, [],
[{:=, [],
[{:tmp2, [], EEx.Engine},
{:<>, [context: EEx.Engine, import: Kernel], ["", "a"]}]}, nil,
{:tmp2, [], EEx.Engine}]}, "b"]}
The above templates result in an identical constant expression. In none of the cases above it’s actually compiled to a constant, and in the second case it compiles into something with even more binary concatenations than the first.
Can I trust Elixir to optimize the empty <% %>
blocks away later on?
Now, my motivation. I’m writing a template engine that compiles to EEx. It’s pretty much a clone of ExPug (the working name is Husky), but with a slightly different syntax and a different architecture. Just like ExPug, I’d like to keep the same line numbers so that the user gets the right line number in case of errors, while not introducing any newlines in the HTML that may be significant (https://hexdocs.pm/expug/line_number_preservation.html)
One way of doing this is to introduce EEx blocks with a newline ("<%\n%>"
). This is a clean solution, (and what ExPug does. I’d like to copy it, but only if this doesn’t cause any significant overhead.
For example, this template:
div.post-list
= for post <- posts do
div.panel.panel-default
div.panel-header
= post.header
div.panel-body
= post.content
div.panel-footer
= post.author
- end
currently compiles to this:
<div class="post-list"><%
%><%= for post <- posts do %><%
%><div class="panel panel-default"><%
%><div class="panel-header"><%
%><%= post.header %></div><%
%><div class="panel-body"><%
%><%= post.content %></div><%
%><div class="panel-footer"><%
%><%= post.author %></div></div><%
%><% end %></div>
This keeps the line numbers without introducing any newlines. But if this causes a lot of overhead, then I might change it to include actual newlines when possible:
<div class="post-list"
><%= for post <- posts do %><%
%><div class="panel panel-default"
><div class="panel-header"
><%= post.header %></div
><div class="panel-body"
><%= post.content %></div
><div class="panel-footer"
><%= post.author %></div></div
><% end %></div>
This adds newlines that have no semantic meaning when possible and defaults to adding <%\n%>
when needed (here it was only needed once). The EEx output is actually cleaner, but it generates larger files with extra newlines for no good reason. I know the overhead here is negligible, but every bit you save can make a different when sending stuff through the wire (yes, I know it can be gzipped and all that). If Elixir optimizes away the empty blocks, then I’ll go with the first approach. If it doesn’t, I’m not really sure what to do… I’d be trading extra string concatenations for larger transfer volumes.
Does anyone here have any tips?