(Sibilant: JavaScript with a Lisp)

github.com/jbr/sibilant

Welcome!

Sibilant is a lisp-family language that compiles to readable and idiomatic JavaScript. Sibilant is built on a simple macro system that gives you compile-time control over the output JavaScript as well as providing tools to smooth over some of JavaScript's historical idiosynchracies.

All of the code in the left column is editable and is compiled by your browser as you type.

Here's an quick sample of sibilant code (inspired by the first example on coffeescript.org).

And here's the canonical node example from nodejs.org

Numbers and Strings

Numbers should look pretty familiar. Use commas to enhance readability.

Strings are surrounded by double quotes. Multi-line strings are supported! Use a backslash to escape a double-quote inside of a string.

Identifiers

Variable names may include letters, the period (.) and the hyphen (-). Lower case letters are idiomatic. They may end with a question mark (?) or with a bang (!). They may start with a sigil ($).

Quoting

Sibilant supports single-word quoting with a leading single quote.

Objects and Arrays

Objects and Arrays are supported as a proper subset of JSON.

Idiomatic sibilant skips any unnecessary commas and colons that do not contribute to readability

Arrays and objects contents are accessed with the get and set macros

Defining variables

Since sibilant is a tool for writing JavaScript, it exposes the var keyword with an identically named macro.

To modify an existing variable (assignment), use the assign macro.

Defining functions

Sibilant avoids hoisiting by defining functions as variable by default.

If your function name includes a dot, it will not be defined as a variable.

Lambdas and Thunks

Lambdas are how sibilant defines anonymous functions.

Because anonymous functions are is used so often in JavaScript, sibilant includes a shortcut to define lambdas, #.

Thunks are zero-arity lambdas (anonymous functions that accept no arguments). A shortcut for this is the #> macro.

Conditionals

Sibilant provides two primary conditional macros: if and when. When is the simplest, only executing the subsequent block when the conditional evaluates to a truthy value. All conditionals in sibilant are expressions and evaluate to some value. This is done by introducing a scope with a self-executing function.

The `if` macro supports two branching paths. If either of the branching paths needs more than one expression, the `do` macro is used.

For more complex if/else branching, sibilant supports the `if-else` macro.

Iteration

Iteration is an example of a macro that can easily be redefined for different compilation contexts. Since the default compilation environments are modern browsers and node, the `each` macro compiles to `[].forEach`. If you needed to compile sibilant to an older JavaScript, the each macro could be easily modified to support a `for` loop.

Chaining

It is often idiomatic in JavaScript to chain function calls. The `chain` macro makes this beautiful and easy in sibilant. Here's an example that uses d3.

Some JS libraries heavily rely on chaining, such as jQuery. For those libraries, instead of having to repeatedly write:

Sibilant provides the `chainable` macro, which shadows the `$` function call with a macro that applies additional aruguments to chain.

Macros

Don't like the JavaScript sibilant outputs? Unlike other compile-to-js languages, one of the primary language features of sibilant is to empower you to write beautiful JavaScript in your preferred idiom. Find you're writing a particular pattern often? Write a sibilant macro to reduce this inefficiency.

Here's an example from angular. Because of the Angular dependency injection system, angular users have to write functions like this example from the angular docs :

In sibilant we can extract this pattern to a macro that is far more DRY. We just have to include this macro at the top of our files, and for the rest of the compiler pass we can use `ang-def` as if it were a native language feature. Don't worry about understanding the macro for now.

You can also access libraries like this with the include macro. On node, include uses the require path system, so you can package up a suite of macros and provide them through npm. The include can also drop utility functions at the top of a file like a header. This is difficult to demonstrate in an in-browser compiler, but incredibly powerful. Real-world usage precompiles sibilant to javascript server-side.