Skip to content

Commit

Permalink
Update for 0.10 release
Browse files Browse the repository at this point in the history
  • Loading branch information
mightybyte committed Dec 9, 2012
1 parent 8399e00 commit c58a744
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 47 deletions.
65 changes: 65 additions & 0 deletions blogdata/content/2012/12/9/heist-0.10-released.md
@@ -0,0 +1,65 @@
| title: Heist 0.10...now with a >3000x performance improvement
| author: Doug Beardsley <mightybyte@gmail.com>
| published: 2012-12-09T20:55:00+0100
| updated: 2012-12-09T20:55:00+0100
| summary: Release notes for Heist 0.10

The Snap team is excited to announce the release of Heist 0.10. For this
massively backwards-incompatible release we went back to the drawing board and
re-wrote Heist from the ground up with performance in mind. But was Heist
actually slow? Nobody has actually complained about it, but yes, Heist was
slow. Well...it was until now.

![](/media/img/heist-perf.png)

Note that this chart is log scale. The difference is so drastic that if the
chart was linear scale, the bars for Heist 0.10 wouldn't even show up! The
three pages benchmarked are taken directly from snapframework.com, so they are
real-world examples. Heist 0.10 gives an improvement of more than 3000x for
snaplets.tpl, 2000x for about.tpl, and 700x for faq.tpl. The improvement for
faq.tpl is smaller because most its content is statically generated by pandoc,
so Heist only has to traverse a small DOM compared to the other two templates.

To understand what happened here, we need to go back to the beginning. When
we originally wrote Heist, speed was not our goal. Instead we wanted to see
how far a simple concept like binding Haskell code to HTML nodes could take us
in solving the problem with HTML boilerplate. Now, two and a half years
later, we've discovered that it's an incredibly powerful and enjoyable model
for web programming.

Heist achieves this by traversing the DOM, executing splices to generate
replacement nodes, and recursively traversing those nodes until all splices
have been executed. A significant part of Heist's power lies in the fact that
this transformation happens at runtime every time a template is served, so the
splice computations are running in your web server monad and have access to
all the data from the HTTP request. Once this transformation is complete, the
resulting DOM is rendered to a ByteString and served back in the HTTP
response. This is really slow when compared with concatenative template
systems like StringTemplate that just blast out interleaved static and dynamic
chunks of data. Therefore it should be no surprise that splicing is slow, as
it allows for much more expressiveness and power.

However, we realized that a lot of the transformations could be done at load
time and be preprocessed to an intermediate representation. This consists of
static ByteStrings interleaved with deferred dynamic computations that still
give you the power of being able to access the web server's runtime monad.
This "compiled splice" as we call it works quite a bit differently from the
old model of splices. It is also strictly less powerful, so we kept the
ability to use the old "interpreted splice" model if you really need the power
and can afford the performance penalty.

While we were making backwards incompatible changes, we decided to clean up
everything. This included reorganizing the modules and any other changes we
felt would improve the overall quality and readability of the code base. It
will be a bit of work to migrate existing code to Heist 0.10, but the old
interpreted splices are still available and allow you do a gradual transition.
We also added a new feature called attribute splices that allows you to
abstract attributes in ways that weren't possible before.

For more detailed information about the new features, check out the tutorials
in the [heist section](/docs#heist) of our docs page. After you have read
those and are ready to upgrade your site, you might want to follow our [migration
guide](https://github.com/snapframework/heist/wiki/Migrating-Snap-Applications-to-Heist-0.10)
to help you with the transition to 0.10. The migration guide is a github
wiki, so feel free to help us make it clearer and more complete.

2 changes: 1 addition & 1 deletion snap-website.cabal
Expand Up @@ -20,10 +20,10 @@ Executable snap-website
blaze-html,
bytestring,
containers,
data-lens-template >= 2.1 && < 2.2,
directory,
filepath,
heist >= 0.10 && < 0.11,
lens >= 3.7 && < 3.8,
MonadCatchIO-transformers >= 0.2 && < 0.4,
mtl >= 2 && <3,
process,
Expand Down
59 changes: 31 additions & 28 deletions snaplets/heist/templates/docs.tpl
Expand Up @@ -3,64 +3,67 @@
<static>
<div id="about" class="section left">
<div class="inner">
<h2>Tutorials</h2>
<h2 id="snap">Snap</h2>
<div id="docdls">
<dl>
<dt><a href="docs/quickstart">Quick Start</a></dt>
<dd>
<a class="book" href="docs/quickstart">
<img src="/media/css/book.png" />
</a>
<a class="book" href="docs/quickstart"><img src="/media/css/book.png" /></a>
A guide to getting Snap installed.</dd>

<dt><a href="docs/tutorials/snap-api">Snap API Introduction</a></dt>
<dd>
<a class="book" href="docs/tutorials/snap-api">
<img src="/media/css/book.png" />
</a>
<a class="book" href="docs/tutorials/snap-api"><img src="/media/css/book.png" /></a>
A quick tutorial on the Snap API. Covers installation, the
&ldquo;snap&rdquo; command-line tool, and a walkthough of the
Snap starter application.</dd>

<dt><a href="docs/tutorials/heist">Heist Template Tutorial</a></dt>
<dd>
<a class="book" href="docs/tutorials/heist">
<img src="/media/css/book.png" />
</a>
A tutorial for the Heist HTML templating library.</dd>

<dt><a href="docs/tutorials/snaplets-tutorial">Snaplets Tutorial</a></dt>
<dd>
<a class="book" href="docs/tutorials/snaplets-tutorial">
<img src="/media/css/book.png" />
</a>
<a class="book" href="docs/tutorials/snaplets-tutorial"><img src="/media/css/book.png" /></a>
Guide to using snaplets to build reusable web components.</dd>

<dt><a href="docs/tutorials/snaplets-design">Snaplets Design</a></dt>
<dd>
<a class="book" href="docs/tutorials/snaplets-design">
<img src="/media/css/book.png" />
</a>
<a class="book" href="docs/tutorials/snaplets-design"><img src="/media/css/book.png" /></a>
Description of the snaplets internal design and motivation.</dd>
</dl>
</div>

<h2 id="heist">Heist</h2>
<div id="docdls">
<dl>
<dt><a href="docs/tutorials/heist">Heist Template Tutorial</a></dt>
<dd>
<a class="book" href="docs/tutorials/heist"><img src="/media/css/book.png" /></a>
A tutorial for the Heist HTML templating library. This tutorial
applies to Heist 0.8 and earlier.</dd>

<dt><a href="docs/tutorials/compiled-splices">Compiled Splices Tutorial</a></dt>
<dd>
<a class="book" href="docs/tutorials/compiled-splices"><img src="/media/css/book.png" /></a>
Discusses compiled splices, which were introduced in Heist 0.10.</dd>

<dt><a href="docs/tutorials/attribute-splices">Attribute Splices Tutorial</a></dt>
<dd>
<a class="book" href="docs/tutorials/attribute-splices"><img src="/media/css/book.png" /></a>
How to use attribute splices (also introduced in Heist 0.10).</dd>

</dl>
</div>
</div>

<div class="inner">
<h2>Resources</h2>
<h2 id="resources">Resources</h2>
<div id="docdls">
<dl>
<dt><a href="docs/style-guide">Haskell Style Guide</a></dt>
<dd>
<a class="book" href="docs/style-guide">
<img src="/media/css/book.png" />
</a>
<a class="book" href="docs/style-guide"><img src="/media/css/book.png" /></a>
A guide to the Haskell source style we're using for the project.</dd>
<dt><a href="/benchmarks">Benchmarks</a></dt>
<dd>
<a class="book" href="/benchmarks">
<img src="/media/css/book.png" />
</a>
<a class="book" href="/benchmarks"><img src="/media/css/book.png" /></a>
Some benchmark results comparing Snap to several other
web frameworks.</dd>
</dl>
Expand All @@ -70,7 +73,7 @@

<div id="about" class="section left">
<div class="inner">
<h2>API Documentation</h2>
<h2 id="api">API Documentation</h2>

<div id="docdls">
<dl>
Expand Down
58 changes: 58 additions & 0 deletions snaplets/heist/templates/docs/tutorials/AttributeSplices.lhs
@@ -0,0 +1,58 @@
Attribute Splices
=================

Attribute splices are new in Heist 0.10. They solve the problem of wanting to
be able to dynamically make empty attributes appear or disappear with a splice
without binding a splice to the whole tag. This issue comes up most
frequently when dealing with empty attributes such as HTML's "disabled" or
"checked".

> module Heist.Tutorial.AttributeSplices where
> import Heist.Tutorial.Imports

Consider a page with several radio buttons. You want the correct one to be
selected based on the value of a parameter in the HTTP request. The HTML
would look something like this:

<input type="radio" name="color" value="red" checked>Red</input>
<input type="radio" name="color" value="green">Green</input>
<input type="radio" name="color" value="blue">Blue</input>
We want to automatically generate the "checked" attribute appropriately. This
could be done with a splice bound to the input tag, but there might be a
number of other input tags on the page, so your splice would at best be
executed on more tags than necessary and at worst not have the granularity
necessary to work properly. The ${} syntax for splices inside of attribute
values also won't work because it can only affect an attribute's value. It
can't make the attribute disappear entirely. This problem can be solved
nicely with attribute splices that have the following type:
< type AttrSplice m = Text -> m [(Text, Text)]
An attribute splice is a computation in the runtime monad that takes the value
of the attribute it is bound to as its argument and returns a list of
attributes to substitute back into the tag. Here's how we might implement a
splice to solve the above problem.
> autocheckedSplice :: Text -> StateT Text IO [(Text, Text)]
> autocheckedSplice v = do
> val <- get -- app-specific retrieval of the appropriate value here
> let checked = if v == val
> then [("checked","")]
> else []
> return $ ("value", v) : checked
In this toy example we are using `StateT Text IO` as our "runtime" monad where
the Text state holds the value of the radio button that should be checked. We
assume that the current value we're checking against is passed as the bound
attribute's value, so we compare that against the value to be checked. Then
we return a list with the appropriate value and the checked attribute if
necessary. We bind this splice to the "autocheck" attribute by adding it to
the hcAttributeSplices list in HeistConfig.
To make everything work we use the following markup for our radio buttons:
<input type="radio" name="color" autocheck="red">Red</input>
<input type="radio" name="color" autocheck="green">Green</input>
<input type="radio" name="color" autocheck="blue">Blue</input>

0 comments on commit c58a744

Please sign in to comment.