Javascript

Metaprogramming

and you

May 14 2014, Verona

Massimiliano Mantione

About Myself

An enthusiast software engineer

Passionate about languages and compilers

Worked in the V8 team in Google

Overall, worked on JIT compilers for +7 years

Now working on scalable, fault tolerant,
web facing distributed systems

Javascript and me

I started as a Javascript hater

When I sow Javascript for the 1st time (in 1995) I vowed that I would never touch such an abomination, even with a 10 feet pole

Eventually, I changed my mind, but...

Let's change subject!

Metaprogramming

Do you know what metaprogramming is?

Are you sure?

It is not "code that can see your code"...

It is "code that can modify your code"!

Please read "Beating the Average"
by Paul Graham

Expressive power

Structured control flow
(if then else, while...)

Record and arrays (dynamic?)

First class functions

Lexical scoping (closures!)

Automating memory management

Metaprogramming!

MACROS

Metaprogramming is writing code that modifies code

A piece of code that modifies other code is called a macro

To write a macro, code must be handled as data

Therefore a macro is a function that takes code as input and produces code as output

Let's experiment

Enter

Metascript

Do we really need
one more language?

What's the Problem with Javascript?

Too many to mention

Convertion rules can kick in with WAT? results

Forgetting a variable declaration can be catastrophic

Lack of proper block-scoping (until ES6)

Verbose Java-like syntax

No type system and no metaprogramming

How Can They Be Solved?

Sticking to "the good parts"

Using jslint or jshint

Or... with a different programming language!
(like Coffeescript)

Javascript Alternatives

Coffeescript
(nice, but no metaprogramming)

Typescript
(has a type system, but is still Javascript)

Lispyscript
(semi-unknown, and is a Lisp)

Clojurescript
(not very Javascript-friendly, and is also a Lisp)

My Wishlist

Clean syntax (like Coffeescript)

Sane scoping rules (like ES6 let)

A type system (like Typescript)

Real metaprogramming (like Lisp)

Be a Good Javascript Citizen

Good Javascript Citizen

Designed for
zero runtime overhead

Each core construct cleanly translates into a Javascript primitive

Consuming existing modules is natural

Producing modules of any kind is easy

Full source map support, from day 1

A Taste of
Metascript!

Our all-time favorite first program:

console.log 'Hello, Metascript!'

Simple Expressions

...Just Work!

(1 + 2 * 3).should.equal 7

('a' + 'b' + 'c').should.equal "abc"

(typeof (1 + 2)).should.equal 'number'

(typeof {}).should.equal 'object'

Look, Ma, No Statements...

...Just Expressions!

(if true 1 else 2).should.equal 1

(do
  var a = 'a'
  a = a + a
  a
).should.equal 'aa'

Loops Are Statements, Too

var fact = (x) ->
  loop (var r = 1, x)
    if (x > 0)
      next! (r * x, x - 1)
    else
      r
      
fact(1).should.equal(1)
fact(5).should.equal(5 * 4 * 3 * 2 * 1)


The Syntax Dilemma

Two main conflicting goals:

  • Being homoiconic
  • Being readable!

Also in the wishlist:

  • Support different coding styles
    • Use parenthesis
    • Be indentation based
  • Be "diff friendly" if needed

The Metascript Solution

Note that expression grouping and nesting can be achieved in different ways

  • With parenthesis and commas
  • With indentation
  • With infix operators of different precedences
  • With "dependent keywords" (like else or catch)

Metascript uses all of them interchangeably!

These Are All Equivalent

callFunction(a, b, c)

callFunction
  (a, b, c)
  
callFunction
  a
  b
  c

These Are Also Equivalent

if condition (f a) else (g b)

if condition
  f a
else
  g b
  
if condition f(a)
else g(b)

You Can Add Parenthesis...

...if you feel you need them

callFunction (
  a
  b
  c
)
if condition (
  f(a)
) else (
  g(b)
)

Sometimes Parenthesis are Mandatory

callFunction (a, b, c)

callFunction a b c

callFunction (a) (b) (c)

( (callFunction(a)) (b) ) (c)

Tuples!

var (a, b) =
  if true (1, 2) else (3, 4)
a.should.equal 1
b.should.equal 2

(a, b) = (b, a)
a.should.equal 2
b.should.equal 1

Blocks give values

var status =
  if ok do
    console.log 'Starting up'
    engine.power = 100
    give! 'moving'
  else do
    console.log 'Stopping'
    engine.power = 0
    'stopped'

Another way to write it

(var status, var message, engine.power) =
  if ok
    ('moving', 'Starting up', 100)
  else
    ('stopped', 'Stopping', 0)
console.log message

Clean Scoping Rules

var r = []
loop (var i = 1)
  if (i > 10) end
  var s = 'element' + i
  r.push s
; These will not compile...
console.log i
console.log s

Write hygenic Macros!

Sometimes inside macros you need to create variables that will be added to the program code

You risk to use a variable name already in the scope where the macro is expanded

Multiple expansions of the macro in the same scope risk to overwrite the variable

Metascript allows you to have variable with "virtual" names that will be transformed so they are guaranteed to be uinique

What's Next?

Making the compiler production-ready

Completing the metaprogramming system

Writing "standard" macros

Implementing the type system

Completing the compiler

It is already pretty solid

Add support for for ... in
(Object.keys() is not always enough)

Running in the browser and implement a REPL shell are not on my radar
(the Lighttable plugin is IMHO even better)

Completing the metaprogramming system

Document the AST API that can be used inside macros.

Polish the module system for macros

Fix a stupid issue with hygienic macros

Fix another stupid issue with quote and unquote

Writing "standard" macros

We already have a lot of them
(Destructuring assignments, structure matchers, argument splicing, switch/case, many small operators...)

I should publish a module on github for them

Eventually add proper generator and coroutine support

Implementing the type system

Take structural subtyping from Typescript

Take convention on type argument names from Haskell

Put real algebraic types into the mix

Allow explicit typing of the this argument

Make everything optional, with type inference

Nice, but...

is that all?

Language evolution

It is damn too slow!

ES7 will likely have async for Promises

Will it ever support rx.js?

Will it ever support async machines with persistent state?

Will it ever support what you need now?

Final words

You should never wait for the ECMA committee,
browser vendors or some compiler writer
to extend your programming language

You should add the features you need to your language

It should take only a handful of lines of code

If you cannot do it,
you are using the wrong language!

That's All, Folks

Metascript

is waiting for you!

code, docs and slides are on github

twitter: @M_a_s_s_i, #metascript

group: metascript@googlegroups.com

Thanks for following!