I wanted a better syntax for JavaScript, but alternatives like coffee-script are basically separate languages. That's not what I wanted, so I made Papuascript. Papuascript retains all the basic elements of JavaScript, with a few differences intended to help keep track of side effects, and generally make function-oriented programming a little easier.
Papuascript takes many syntactic ideas from Haskell.
- Line-for-line translation to JavaScript, for easy debugging.
- Two-way translatable - every JavaScript program has a Papuascript translation.
- No new reserved words (per above)
- Greatly reduces the need for punctuation without introducing ambiguity.
- Very concise function definition.
- Significant whitespace, with simple rules.
- The
:=operator is used for assigning values to variables defined in an outer scope, helping you keep track of side effects. - Assignments have no return value. This is not a bug, it's a feature. Seriously.
- Nested functions can be made more readable using
<-. This allows you to make code written in continuation-passing style look more like standard imperative code. This is similar to the "do" syntax in Haskell. - TODO: Allow significant whitespace to be suspended, a la
Haskell's
{}syntax
| JavaScript | Papuascript |
|---|---|
var a = 5 |
a = 5 |
a = 5 |
a := 5 (only for variables in outer scope) |
foo(bar, spam) |
foo bar spam |
foo() |
same |
var foo = bar |
foo = bar |
a === b |
a == b |
a == b |
no equivalent |
a = b = c |
a = c, b = c |
while (row = getRow()) { |
while row = getRow(); row |
a ? b : c |
?? a : b : c |
void 0 |
undefined |
a[2] |
a.2 or a[2] |
// comment |
same |
/* comment */ |
same (but can be nested) |
var x, y |
same - use to set scope w/o assigning a value |
Javascript:
var add = function(x, y) {
return x + y;
};Papuascript:
add = \x y -> x + y
Javascript:
for (var i = 0; i < 5; i++) {
blah(i);
}Papuascript:
for i = 0; i < 5; i++
blah i
See doc/SyntacticSugar.md
An indented block begins with a line that is more indented than the line above it; the indented block ends with (but does not include) the first line less indented than the block.
if x
y = x // begin indented block A
if x > z
foo // indented block B
z = y.bar // end indented block A
spam()
The line above an indented block is the parent line. The indented block is interpreted as either a continuation of the parent line or as a proper block belonging to the parent line, according to the following rules:
- An indented block is a block if:
a. Its parent line ends in
->b. Its parent line begins with a block-forming keyword. These keywords are: - if - else - for - while - do - switch - case - default - try - catch - finally - Otherwise, the block is a continuation of the parent line.
while foo == bar
// this is a block
doStuff()
doOtherStuff()
bar = newBar()
dinner = corned
beef +
cabbage // this is a single statement
f a b c becomes f(a,b,c) in Javascript. To call a function
with no arguments, use f().
Function calls have higher precedence then operators.
| Papuascript | JavaScript |
|---|---|
a b c + foo bar * spam |
a(b, c) + foo(bar) * spam |
a b (foo + bar) c |
a(b, foo + bar, c) |
A dot . preceded immediately by a space has lower
left-precedence than a function call. Thus,
foo bar .spam eggs is equivalent to (foo bar).spam eggs.
This is to allow for method-chaining without parentheses.
Papuascript attempts to separate statements used for their
side-effects from statements that return a value. Assignments
have effects, so x = y does not return a value in Papuascript.
Something like a = b = c will generate a syntax error. The same
is true for other assignment operators, such as +=, ++, and
:=. Likewise, for, while, export, and import have no
return value.
These constructs do return a value. In each case, the value returned will be the value of the last statement executed.
The following will assign the value 1 to the variable x:
counter = 3
x =
if counter < 0
-1
else if counter > 0
1
else
0
\x -> x * x becomes function(x) { return x * x; } in
JavaScript.
The return value of a function is the value of the last statement
evaluated in it. If that statement is one that returns no value,
such as an assignment, a for-loop, or a while-loop, it returns
undefined.
a = 4 assigns 4 to a local variable a. It is equivalent to
the JavaScript var a = 4. To alter the value of a variable from
an outer scope, you must use b := 4. Note that function
arguments and this refer to variables outside the current
scope.
In JavaScript, there are often times when a function literal is passed as the last argument to another function. This is especially common when attaching an event listener, or any time one is using a function that employs continuation-passing style. To make this easier, and to assist the coder who wants to employ a more function-oriented approach, Papuascript takes a cue from Haskell. The following two pieces of code are equivalent:
updateOnClick = \domElement ->
domElement.addEventListener 'click' \e ->
if checkEventIsOK e
domElement.update()
updateOnClick theBigButton
is equivalent to
updateOnClick = \domElement ->
e <- domElement.addEventListener 'click'
if checkEventIsOK e
domElement.update()
updateOnClick theBigButton
Note that the function body extends to the bottom of the block,
so no statements can be put after the call to
domElement.addEventListener. Thus, this syntax is mainly useful
in cases where continuation passing is being used heavily.
# encloses the rest of the line in parentheses.
Cases do not fall through, so there is no need to use the break
statement there. To associate multiple cases with a single block,
separate them with commas: case 1, case 2