Personal tools


From HaskellWiki

Revision as of 14:43, 9 July 2010 by Gbaz (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Jmacro is a library for the programmatic generation of Javascript code. It can be used to directly write javascript, and as a backend for combinator libraries and domain specific languages which target javascript. The jmacro package also provides a standalone executable, which may be used to compile jmacro syntax directly to Javascript.

Jmacro is available from hackage: A source repository is available from patch-tag:


1 Key Features

2 = Syntax Checking

Jmacro provides compile-time guarantees of syntactic correctness. Any jmacro code which compiles will necessarily produce syntactically correct Javascript. Note that jmacro itself is untyped, which means that the generated javascript may not be typesafe. This is a feature, as a great deal of useful javascript code is either hard to type, or simply not statically typeable. Take, for example, the commonly used jQuery library. Depending on the string argument to the $ function, the returned value can vary over a range of types.

2.1 Functional Syntax

Jmacro provides a mixed-functional syntax. Nearly all valid javascript code is also valid jmacro code. Code from the documentation of an existing library can be copied and pasted directly into Haskell as a jmacro block. However, jmacro also provides a great deal of syntactic sugar that allows code to be written in a considerably more functional style, akin to Haskell or ML. Notably, anonymous functions may be introduced with a single backslash, and function application may be denoted by simple whitespace. Hence, this is valid jmacro describing a function which doubles a number:

\x -> x + x

2.2 Hygienic Names, Proper Scoping

It is common, when generating Javascript, to want to compose blocks of code which operate on different parts of a page. These blocks of code may find it necessary to introduce top-level variables for sharing state. Typically, this is resolved by placing these blocks of code within an anonymous function to provide namespacing. When two blocks of jmacro code are composed, however, variable declarations within each will *automatically* be named in the generated code so as not to overlap. This additionally allows jmacro to provide proper block-level scoping, which does not exist in standard Javascript.

Thus, in the following example, the two declarations of `var x` produce *different* identifiers in the generated javascript:

renderJs $ [$jmacro|var x;] `mappend` [$jmacro|var x;]
var jmId_0;
var jmId_1;

2.3 Antiquotation, Marshalling and Unmarshalling, Shared Scope

The ability to provide hygienic names becomes particularly useful when coupled with antiquotation. Within a Haskell program, quoted jmacro code blocks may contain antiquotations, which themselves contain Haskell code. The antiquoted Haskell code may either produce a jmacro expression or statement itself, or it may produce a Haskell value which can be marshalled to jmacro (i.e., is an instance of the ToJExpr class).

Marshalling a number:

> let foo = 5 in renderJs [$jmacro|var x = `(foo)`;|]
var jmId_0;
jmId_0 = 5;

Marshalling a string:

> let foo = "hello" in renderJs [$jmacro|var x = `(foo)`;|]
var jmId_0;
jmId_0 = "hello";

Passing in another expression:

> let foo = [$jmacroE|alert "yo"|] in renderJs [$jmacro|var x = `(foo)`;|]
var jmId_0;
jmId_0 = alert("yo");

Antiquoted Haskell code has full access to all identifiers in scope within the enclosing jmacro block. This allows for the construction of macros:

> let foo = \x -> [$jmacroE|alert `(x)`|] in renderJs [$jmacro|var x = "hello"; var y = `(foo x)`;|]
var jmId_0;
jmId_0 = "hello";
var jmId_1;
jmId_1 = alert(jmId_0);