A Fast Retargetable Template Engine written by Alvaro J. Genial in Haskell.
Neat is a slightly unusual templating tool that does not interpret templates at runtime; rather, it compiles templates statically from the source (input) language into the target (output) language. In that sense neat
can be thought of as a very flexible preprocessor masquerading as something fancier. Of course, the result can then be embedded or compiled directly in order to accept arbitrary data at runtime and transform it as desired, dynamically.
There are advantages and disadvantages to this approach; one of the nice side-effects is that neat
has the property of being self-hosting.
Neat is still experimental and likely to change in the future.
Syntax matters.
Templates can bridge the gap between different audiences understanding a shared output specification. Neat is an attempt to broaden the set of choices available in this endeavor while offering the opportunity for increased type safety and performance.
The only requirements are the base
, filepath
and parsec
packages. It is tested with GHC 7.4 and 7.6, but in theory doesn't require them—please open an issue for compatibility problems.
$ cabal install neat
...
$ git clone https://github.com/ajg/neat.git
...
$ cd neat
$ ghc Main -o neat
...
$ curl -LOk https://github.com/ajg/neat/archive/master.zip
...
$ unzip master.zip
...
$ cd neat-master
$ ghc Main -o neat
...
The command-line tool is very rudimentary at the moment.
neat --hs Foo.hs.neat
will produce a (Haskell) file namedFoo.hs
neat --xml Foo.hs.neat
will produce an (XML) file namedFoo.hs.neat.xml
neat --xslt Foo.hs.neat
will produce an (XSLT) file namedFoo.hs.xsl
- Templates can be compiled directly in the target language, which can produce very fast output.
- Templates can be verified and type-checked in the target language, eliminating a large class of errors.
- Templates can be given data in its natural (however typed) form, with no need for an intermediary.
- Template functionality can be extended easily and seamlessly using any facility (e.g. functions) available in the target language.
- Processing templates requires an additional compilation step (though any decent build system can mitigate the issue.)
- Creating dynamic templates is difficult in statically-compiled languages.
The main executable is just a thin wrapper around the library; what it produces does not itself rely on Haskell—see the usage section.
The library is a set of Haskell modules that can be combined to parse, manipulate and generate templates; the library can be used independently of the command-line tool, though its use is limited to Haskell, whereas the rest of the system is not.
This is a module with common utilities for input modules.
These are library modules that take text, parse it, and produce a template from it. There's only one parser currently:
- Django: modeled after Django's template system.
This is a module with common utilities for output modules as well as the Output
type class, which encapsulates the way values are output from Haskell, and the Zero
type class, which determines what values are considered "false/empty/null/nil."
(Note: At the moment the module contains a {flexible, overlapping, undecidable, possibly incoherent} instance of Output
that automatically grandfathers anything with a Show
instance into it for convenience. Since that behavior is probably overreaching in a lot of cases, it will likely be factored out and moved to an output-specific utility module. Possibly the same goes for Zero
.)
These are library modules that take a template and generate text from it. The current choices are:
- Haskell: the result is Haskell code (typically either a module or the main program.)
- XML: the result is an XML representation of the input template.
- XSLT: the result is a (theoretical) XSLT parallel of the input template.
This module contains data types for a relatively abstract, low common denominator template specification; it's roughly what XML Infoset is to concrete XML.
Neat is built using itself: the original (hand-crafted) output generator, Haskell0.hs
, is replaced by a version generated from a neat
template, Haskell.hs.neat
. Similarly, other output generators use neat templates to produce executable Haskell.
Neat can be extended in various ways. In ascending order of difficulty:
- Add an output module: copy one of the existing ones and go from there; then run
./neat --hs
on it (if it's a Neat template—it doesn't have to be); finally, choose a flag & extension and add it toMain.hs
. - Add an input module: copy the existing
Django
module and alter the syntax (built onparser
) to suit the desired input format (or use a different parsing mechanism); then add a way to use it toMain.hs
. - Extend and/or modify the generic
Template
schema; this will require changes to all output generators at a mininum (including re-running./neat --hs
on the template-based ones), otherwise there's a high chance of inexhaustive patterns (i.e. nasty partial functions) or outright compilation errors due to missingOutput
instances; it may require changes to input parsers as well, depending on the change.
- More intelligent and/or precise whitespace/newline elision
- Escape transliterated comments
- Clean up emitted code so that hlint doesn't complain
ByteString
/Data.Text
support- Proper parsing of bindings and values
- Proper parsing of command line flags
- More documentation
- Tests
- Use type singletons or data kinds to make Output language-dependent
This library is distributed under the MIT LICENSE.