Monday, May 23, 2011

Happstack + JMacro + HSX

Happstack (a Haskell web framework) has long had support for using HSX so that you can use XML syntax in your Haskell page templates. Happstack now has support for using JMacro to add javascript to your templates.

JMacro was written by Gershom Bazerman, and was first released in July of 2009. Our contribution is the creation of two glue libraries:
hsx-jmacro
a thin wrapper which makes it easy to embed JMacro into HSX (independent of Happstack).
happstack-jmacro
another thin wrapper that makes it easy to generate and serve JMacro .js scripts using Happstack

The use of Happstack+HSX+JMacro has been documented in detail in this section of the Happstack Crash Course.

Here is a little example of what Happstack+HSX+JMacro looks like:
> helloJMacro :: JMacroPart Response
> helloJMacro =
>     toResponse <$> defaultTemplate "Hello JMacro" ()
>       <div>
>        <% [$jmacro| 
>            var helloNode = document.createElement('h1');
>            helloNode.appendChild(document.createTextNode("Hello, JMacro!"));
>            document.body.appendChild(helloNode);
>            |] %>
>       </div>
>

As you can see, the syntax used by JMacro is almost identical to JavaScript. So, you do not have to learn some special DSL to use it. In fact, JMacro can work with most JavaScript you find in the wild. Using JMacro has a number of advantages over just using plain-old javascript:
  • syntax checking ensures that your JavaScript is syntactically valid at compile time. That eliminates many common JavaScript errors and reduces development time.
  • hygienic names and scoping automatically and transparently ensure that blocks of JavaScript code do not accidentally create variables and functions with conflicting names.
  • Antiquotation, marshalling, and shared scope make it easy to splice Haskell values into the JavaScript code. It also makes it easy to programmatically generate JavaScript code.

The last point means that instead of trying to concat strings to call JavaScript functions with Haskell values like this:
hello str = <button onclick=("alert(" ++ str ++ ");")>click me</button>

You can write:
hello str = <button onclick=[$jmacro| alert(`(str)`); |]>click me</button>

The first version has a major bug -- it does not properly escape the str which can lead to javascript injection attacks. The JMacro version, on the other hand, automatically escapes the string.

You are not limited to just splicing strings and other primitive types into the JavaScript code. By creating an instance of ToJExpr you can easily pass any Haskell value to JavaScript.

While JMacro can easily be used for simple templating, it is actually a very powerful tool for generating JavaScript. It is essentially Template Haskell for JavaScript. It parses the JavaScript into an abstract syntax tree, and supports quasiquotation and antiquotation.

To learn more about using JMacro with Happstack and HSX, start reading here.

And a big thanks for Gershom Bazerman for creating JMacro. It's not part of Happstack or HSX, so you should have no problems using it with other web frameworks or templating libraries.

Monday, May 2, 2011

MACID Haskell persistent data store overhauled

Over the past few weeks Lemmih has been working on a major overhaul of MACID. MACID is a persistent data store explicitly designed for use with Haskell. It can store arbitrary Haskell data structures and queries are written in straight-forward Haskell (no funky DSLs). It is thread-safe, and supports the classic ACID properties. Unplug your machine with out losing your data.

The homepage for the new MACID library can be found here.

There are two new libraries acid-state and safecopy, which will replace happstack-data and happstack-state in Happstack 7.

safecopy provides versioned binary serialization of datatypes with version migration. It adds this functionality on top of the cereal library.

acid-state provides ACID transactions on top of safecopy.

What's New


The new MACID is like the old MACID, but better. The fundamental concepts are the same, but it is a lot cleaner, more robust and less magical.

The rewrite of MACID addresses many long standing wish-list items, including:

MACID now a separate project


acid-state and safecopy are now free standing libraries, with no references or dependencies on anything Happstack. They have their own maintainer, homepage, source repository, etc. So, now you can used MACID, even if you don't use Happstack. (This has actually been true for a while, but now it is a lot more obvious).

If all you want is versioned binary serialization and migration, then you can use the safecopy library on its own.

update / query take an explicit state handle now


In happstack-state, query and update accessed the state via a global IORef. This meant that you could only have one instance of MACID per application. It also weakened type-checking resulting runtime errors that should have been avoidable.

It also meant that you could only have one ACID store per application.

Now update and query take an explicit handle.

No more Component class


happstack-state has a Component class, which is mostly worthless. It gives the promise of power, but doesn't really deliver anything. It is just extra boilerplate for the most part. This class is gone entirely in acid-state.

Less boilerplate for serialization instances


In happstack-data, creating a Serialization instance for a type required you to call $(deriveSerialize ''Foo) and you have to create an instance Version Foo. With safecopy, you only need one line, '$(deriveSafeCopy 0 'base ''Foo)'

In happstack-data there were three type classes, Serialize, Version, and Migrate. This has been simplified down to just two in safecopy: SafeCopy and Migrate.

Better Safety


happstack-state has a number of corner cases that it does not handle gracefully. For example, bad things happen if update events call fail or error. acid-state handles these correctly and includes a test suite specifically for testing these types of failures.

Additionally, acid-state is a lot better at flushing data to disk.

Performance


Initial performance testing shows that acid-state is fast. A simple data store that holds an integer which is incremented by an update event was able to achieve 13,300 updates per second.

Still Coming


There is still a bunch of work to come.

acid-state currently does not support Windows. This is because the code to safely flush data to disk requires the posix library. Windows support can be added via some #ifdefs. If you are familiar with Windows and file IO and want to help, your contribution will be greatly appreciated.

Migrating data from happstack-state to acid-state is certainly feasible, but the process is not yet documented. But it will be.

There are also major improvements to IxSet planned.

And, of course, replication and sharding. Fortunately, it will be easier to implement these on top of the new acid-state code base.

Get Started Today


acid-state is usable today! You can install it from hackage. There are code examples here and here. If you are starting a new MACID based project, you are encouraged to consider acid-state.

To use happstack-ixset you will currently need to add this extra instance to your code:
import Data.SafeCopy
import Happstack.Data.IxSet            (IxSet)
import qualified Happstack.Data.IxSet  as IxSet

instance (SafeCopy a, Ord a, Typeable a, IxSet.Indexable a) => SafeCopy (IxSet a) where
    putCopy ixSet = contain $ safePut (IxSet.toList ixSet)
    getCopy = contain $ IxSet.fromList <$> safeGet

Once IxSet is factored out of happstack into a separate library that instance will be provided automatically.

It is safe to mix happstack-state and acid-state in the same application if you want to use acid-state in an existing application.

More updates to come as things develop!