Earl Grey is a neat little language that compiles to JavaScript. Here's what it has to offer:

Earl Grey is still in development, be sure to star it on GitHub to show some support!

Also be sure to check out the interactive tutorial.

What does it look like?

Earl Grey is whitespace sensitive: indent defines blocks and line breaks separate statements. This reduces the punctuation noise often seen in other languages in the form of braces and semicolons.

Parts of the language will be very familiar to Python users, for instance this excerpt of a cutting edge rocket launching application in Earl Grey:

var i = 10
while i >= 0:
   if i == 0:
      print "Blast off!"
   else:
      print i
   i--

But EG also takes steps towards a "streamlined" design that removes many of the spurious distinctions other languages make, for instance the distinction between expressions and statements, variable and function declarations, or loops and list comprehensions (which share the same syntax in EG):

fib(n) =
   var {a, b} = {1, 1}
   1..n each i ->
      {a, b} = {b, a + b}
   a

fibs = 0..10 each i -> fib(i)
print 'The first ten fibonacci numbers are {fibs.join(", ")}'

You can read more about EG's many features in the documentation.

Compatible

One of EG's primary goals is to be as compatible as possible with existing JavaScript libraries and frameworks and the node ecosystem.

Any package in npm's wide selection can thus be imported and used without issue. The same goes for jQuery, canvas/SVG libraries, or frameworks such as React. Conversely, EG can be used to create packages that JavaScript code may import and use.

EG has support for source maps. Plugins exist for a few existing frameworks: gulp-earl for gulp, earlify for browserify.

Tooling

Editor support is admittedly mediocre at the moment, but it is in development:

async/await

EG makes asynchronous code a breeze. EG's implementation is based on Promises as defined by ECMAScript version 6 and many libraries already implement this interface. For the rest, existing callback-based functionality can be converted to Promises using promisify.

To give you an idea here is a script to print the alt-text of the first ten XKCD comics, accessed through their JSON API:

require: request
g = promisify(request.get)

async getXKCD(n = "") =
   response = await g('http://xkcd.com/{n}/info.0.json')
   JSON.parse(response.body)

async:
   requests = await all 1..10 each i -> getXKCD(i)
   requests each req -> print req.alt

Calls to getXKCD or g are asynchronous, which means that they don't block. The code above will therefore fetch the data for the first ten XKCD comics in parallel*, not sequentially. await all` collects the results for us, returning only when everything is done (and then we can print them in order).

* Do note that this is only IO parallelism, not true parallelism. You cannot use async/await to run ten matrix multiplications in parallel on different threads or different processors unless you explicitly create these threads (or web workers), or use a library that does.

Pattern matching

Pattern matching is the kind of feature that you can't tolerate not having, once you have a taste of it. A lot of languages implement crippled versions of it (usually as destructuring assignment). EG gives you everything (short of actual static guarantees, but I'm thinking about it).

match can serve as a switch or case statement:

fact(n) =
   match n:
      0 -> 1
      1 -> 1
      n -> n * fact(n - 1)

But we can do better:

fact(match) =
   0 or 1 -> 1
   n -> n * fact(n - 1)

We can extract elements from arrays:

match process.argv[2..]:
   {"install", name}     -> ...
   {"list", query = "*"} -> ...
   {"version"}           -> ...
   ...

From objects:

point = {x = 2, y = 3}
{=> x, => y} = point   ;; x is 2, y is 3

We can do type checking and type coercion:

String? "abc"           ;; ==> true
Array! "abc"            ;; ==> {"abc"}

repeat(String? thing or Array! thing, Number? match) =
   0 -> {}
   n -> thing.concat(repeat(thing, n - 1))

repeat("hello", 4)      ;; ==> "hellohellohellohello"
repeat({1, 2}, 2)       ;; ==> {1, 2, 1, 2}
repeat(6, 3)            ;; ==> {6, 6, 6}
repeat("apple", "pie")  ;; ==> ERROR

More exhaustive documentation can be found here.

Document building

Earl Grey's % operator can be used to easily build HTML, DOM, virtual DOM, and other things:

mul-table =
   div[#multiplication-table] %
      h1 % "Multiplication table"
      table % 1..10 each i ->
         tr % 1..10 each j ->
            td % i * j

The resulting data structure can then be transformed in various ways. For instance, you can use the /html and /dom standard packages:

html builds a string of HTML that you can print or save in a file:

require: /html
print html(mul-table)

dom builds a DOM element that you can append somewhere in your page:

require: /dom
document.get-element-by-id("target").append-child(dom(mul-table))

You can also streamline the operation by importing a specialized % operator to do what you want automatically:

require-macros: /html -> (%)
print strong % "Hello world" ;; ==> "<strong>Hello world</strong>"

React

In addition to HTML and plain DOM conversions, there is also a react package for Earl Grey called earl-react from which you can import a % operator that builds React virtual DOM nodes:

require: earl-react as React
require-macros: earl-react -> (%, component)
component HelloMessage:
   render() = div % 'Hello {@props.name}'
React.render(HelloMessage % name = "Balthazar", mount-node)

It would be straightforward to define % for other frameworks, or for new ones, or to generate other languages such as LaTeX.

Macro system

EG's macro system permits the definition of new control structures that look native to the language. Macros are fairly easy to write and can make code terser and more readable:

inline-macro W(expr):
   `(^expr).split(" ")`

W"apples bananas cantaloupes" ;; ==> {"apples", "bananas", "cantaloupes"}
inline-macro unless(`{^test, ^body}`):
   `if{not ^test, ^body}`

unless 1 == 2:
   print "Everything is fine!"

At the moment I have not yet well documented the macro system, but there is still some documentation here.

Macro libraries

It is possible to define macro libraries with macros importable via require-macros.

In fact, there are already macro libraries for many existing JavaScript or node libraries! Here are some of them:

Test with earl-mocha

earl-mocha defines a certain amount of macros to help you write tests for your applications:

require-macros:
   earl-mocha -> (describe, it, assert, expect-error)

describe "Array":

   it "#concat":
      assert {1, 2}.concat({3, 4}) == {1, 2, 3, 4}

   it "#map":
      assert {1, 2, 3}.map(x -> x * x) == {1, 4, 9}
      expect-error TypeError:
         {1, 2, 3}.map("huh")

Build with earl-gulp

Using earl-gulp, you can define gulp tasks like this:

require-macros: earl-gulp -> task

require: gulp, gulp-sass, gulp-earl, gulp-sourcemaps

task sass:
   chain gulp:
      @src("./content/**/*.sass")
      @pipe(gulp-sass(indented-syntax = true))
      @pipe(gulp.dest("./output"))

task earl:
   chain gulp:
      @src("./content/**/*.eg")
      @pipe(gulp-sourcemaps.init())
      @pipe(gulp-earl())
      @pipe(gulp-sourcemaps.write("./"))
      @pipe(gulp.dest("./output"))

task default < {earl, sass}

Make pages with earl-react

React has its own little language called JSX to define components, but there's no need for this when you have earl-react:

require: earl-react as React
require-macros: earl-react -> (%, component)
globals: document

component TodoList:
   render() =
      ul % enumerate(@props.items) each {i, item} ->
         li % item

component TodoApp:
   get-initial-state() = {items = {}, text = ""}
   render() =
      div %
         h3 % "TODO"
         TodoList % items = @state.items
         form %
            on-submit(e) =
               e.prevent-default()
               @set-state with {
                  items = @state.items.concat({@state.text})
                  text = ""
               }
            input %
               value = @state.text
               on-change(e) =
                  @set-state with {text = e.target.value}
            button % 'Add #{@state.items.length + 1}'

React.render(TodoApp % (), document.get-element-by-id("mount"))

Resources