RapydScript

For those who prefer Python over JavaScript and think CoffeeScript feels more like Perl

Background

  • Started using Pyjamas because I was reluctant to learn JS
  • Wrote an image-recognition drawing app (20k SLOCs)
  • Realized Pyjamas/GWT doesn't scale and JS is not so bad
  • Still had 20k lines of Python code to port
  • Wrote a porting tool based on PyvaScript
  • Realized that I liked this pseudo-language more than JS
  • Added more features to it, others started following it
  • Rewrote the core using UglifyJS2 as inspiration/base

What's wrong with JavaScript?

  • It's not Python

What's wrong with JavaScript?

  • It prefers global scope
  • Its class implementation is counter-intuitive
  • Everything is an object (typeof hell)
  • Funky equality ("0" == false, arrays never equal)
  • Easy to shoot yourself in the foot

What's right with JavaScript?

  • Duck typing
  • Everything is an object (anonymous functions, callbacks)
  • Can run on the client and on the server
  • Can access the DOM
  • Easy to tinker after deployment

What's a Python guy to do?

  • Suck it up and learn JavaScript
  • Suck it up and learn CoffeeScript
  • Learn RapydScript

What's wrong with CoffeeScript?

  • Poor conventions leave room for ambiguity
  • Even easier to shoot yourself in the foot than with JS
  • It's more like Ruby/Perl than Python

Poor Conventions

In English, a comma is a more significant delimiter than a space

Not In coffeescript, however

>>> Not In coffeescript, however



Not(In(coffeescript, however));

Poor Conventions

CoffeeScript

JavaScript

doSomething 1,
  2
  3
  4
doSomething(1, 2, 3, 4);
doSomething 1,
2
3
4
doSomething(1, 2);
3;
4;
doSomething 1,
  2
    3
   4
doSomething(1, 2, 3, 4);

Foot Shooting 101

What does the following code do?

doNothing = (i) ->
x = (n) ->
    for i in [0...n]
        doNothing i
x 1e20

It crashes!

doNothing = function(i) {};

x = function(n) {
  var i, j, ref, results;
  results = [];
  for (i = j = 0, ref = n; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
    results.push(doNothing(i));
  }
  return results;
};

x(1e20);

CoffeeScript conceals details that could lead to serious bugs

Foot Shooting 101

Another implicit return victim:

nextState = off
totalClicks = 0
$ ->
    #Count how many button clicks there have been
    $('form').submit ->
        totalClicks++
        $('#total').text totalClicks
        false
    
    #Toggle when the toggle button is clicked
    $('#btn').click ->
        $('#state').text if nextState then "On" else "Off"
        nextState = !nextState

More like Ruby than Python

  • Implicit returns
  • Cool shortcut operators (->, =>, var?, @)
  • Optional parentheses, commas, brackets
  • More than one way to do things (until, unless, yes, on, true)

Why RapydScript?

  • Combines clarity of Python with compatibility of JS
  • Favors Pythonic design patterns
  • Prevents bad JS habits and bugs ('var', 'new', 'this', etc.)
  • Minimizes context switch for Python developers
  • Full compatibility with all JS features and environments

RapydScript Example

class Rectangle:
    def __init__(self, w=1, h=1):
        self.width = w
        self.height = h
        
    def getArea(self):
        return self.width*self.height

    @staticmethod
    def isRectangle(object):
        return isinstance(object, this)
    
a = Rectangle(5, 10)
a.getArea() == Rectangle.getArea(a)
print(Rectangle.isRectangle(a))

RapydScript Example

function _$rapyd$_print() {
    var args, output;
    args = [].slice.call(arguments, 0);
    output = JSON.stringify(args);
    if ("console" in window) console.log(output.substr(1, output.length-2));
}
function Rectangle() {
    this.__init__.apply(this, arguments);
}
Rectangle.prototype.__init__ = function __init__(w, h){
    var self = this;
    if (typeof w === "undefined") w = 1;
    if (typeof h === "undefined") h = 1;
    self.width = w;
    self.height = h;
};
Rectangle.prototype.getArea = function getArea(){
    var self = this;
    return self.width * self.height;
};
Rectangle.isRectangle = function isRectangle(object){
    return object instanceof this;
};

a = new Rectangle(5, 10);
a.getArea() === Rectangle.prototype.getArea.call(a);
_$rapyd$_print(Rectangle.isRectangle(a));

Feature Set

  • Inverted variable scope
  • Classes and classical inheritance
  • Modules, namespaces and imports
  • Python standard library (makes porting/reuse easier)
  • Code that makes errors more obvious
  • Pythonic syntax sugar
  • decorators
  • *args, **kwargs
  • optional arguments
  • list comprehensions

RapydScript in the Wild

  • GlowScript
  • NodeBox
  • salvatore.diodev.fr/RapydScript
  • rapydscript.com examples
  • rapydscript.com/try-it

Useful Tools

  • github.com/atsepkov/vim-rapydscript
  • github.com/tgienger/language-rapydscript
  • github.com/charleslaw/RapydScript-Decompiler
  • github.com/loolmeh/grunt-rapydscript
  • github.com/vindarel/gulp-rapyd
  • bitbucket.org/railcar88/flask-rapydscript
  • github.com/traverseda/django-pipeline-rapydscript
  • github.com/loolmeh/generator-rui-angular
  • github.com/ivanflorentin/RapydFlux

Homework

Write your next web app in RapydScript

Community

  • rapydscript.com
  • github.com/atsepkov/RapydScript
  • groups.google.com/forum/#!forum/rapydscript

RapydScript

By Alexander Tsepkov

RapydScript

For those who prefer Python over JavaScript and think CoffeeScript feels more like Perl

  • 1,452