Overview
LiveScript is a language which compiles to JavaScript. It has a straightforward mapping to JavaScript and allows you to write expressive code devoid of repetitive boilerplate. While LiveScript adds many features to assist in functional style programming, it also has many improvements for object oriented and imperative programming.
LiveScript is a fork of Coco and an indirect descendant of CoffeeScript, with which it has much compatibility.
npm install -g LiveScript
Follow @gkzahariev for updates on LiveScript.
Featured blog post: LiveScript 1.3.0 released!
Some Examples
LiveScript
# Easy listing of implicit objects table1 = * id: 1 name: 'george' * id: 2 name: 'mike' * id: 3 name: 'donald' table2 = * id: 2 age: 21 * id: 1 age: 20 * id: 3 age: 26 # Implicit access, accessignment up-case-name = (.name .= to-upper-case!) # List comprehensions, destructuring, piping [{id:id1, name, age} for {id:id1, name} in table1 for {id:id2, age} in table2 when id1 is id2] |> sort-by (.id) # using 'sort-by' from prelude.ls |> each up-case-name # using 'each' from prelude.ls |> JSON.stringify #=> #[{"id":1,"name":"GEORGE","age":20}, # {"id":2,"name":"MIKE", "age":21}, # {"id":3,"name":"DONALD","age":26}] # operators as functions, piping map (.age), table2 |> fold1 (+) #=> 67 ('fold1' and 'map' from prelude.ls)
JavaScript
var table1, table2, upCaseName, id1, name, id2, age; table1 = [ { id: 1, name: 'george' }, { id: 2, name: 'mike' }, { id: 3, name: 'donald' } ]; table2 = [ { id: 2, age: 21 }, { id: 1, age: 20 }, { id: 3, age: 26 } ]; upCaseName = function(it){ return it.name = it.name.toUpperCase(); }; JSON.stringify( each(upCaseName)( sortBy(function(it){ return it.id; })( (function(){ var i$, ref$, len$, ref1$, j$, len1$, ref2$, results$ = []; for (i$ = 0, len$ = (ref$ = table1).length; i$ < len$; ++i$) { ref1$ = ref$[i$], id1 = ref1$.id, name = ref1$.name; for (j$ = 0, len1$ = (ref1$ = table2).length; j$ < len1$; ++j$) { ref2$ = ref1$[j$], id2 = ref2$.id, age = ref2$.age; if (id1 === id2) { results$.push({ id: id1, name: name, age: age }); } } } return results$; }())))); fold1(curry$(function(x$, y$){ return x$ + y$; }))( map(function(it){ return it.age; }, table2)); function curry$(f, bound){ var context, _curry = function(args) { return f.length > 1 ? function(){ var params = args ? args.concat() : []; context = bound ? context || this : this; return params.push.apply(params, arguments) < f.length && arguments.length ? _curry.call(context, params) : f.apply(context, params); } : f; }; return _curry(); }
Unnested callbacks and parentheses free chaining:
LiveScript
<- $ 'h1' .on 'click' alert 'boom!'
JavaScript
$('h1').on('click', function(){ return alert('boom!'); });
Installation
You can install LiveScript through the Node Package Manager: sudo npm
install -g LiveScript
.
Alternately, you can also download it (zip, tar.gz),
enter its directory, and run sudo make install
. Using
git to download: git clone
git://github.com/gkz/LiveScript.git && cd LiveScript
&& sudo make install
. Node.js is required to be installed on your
system.
You can also use it directly in the browser by including the
file in LiveScript/browser/livescript.js
via a script
tag. You must then call require("LiveScript").go()
. If
you use this, your LiveScript scripts have to be placed after the
included livescript.js
file, and the script tags must
have the attribute type="text/ls"
.
For example:
<script src="livescript.js"></script> <script type="text/ls"> console.log "boom #{window.location}" </script> <script> var LiveScript = require("LiveScript"); LiveScript.go(); </script>
Usage
Command Line:
Usage: lsc [options]... [file]...
Use lsc
with no options to start REPL.
General:
-v, --version | display version |
-h, --help | display this help message |
-c, --compile | compile to JavaScript and save as .js files |
-e, --eval code::String | pass as string from the command line as input |
-d, --prelude | automatically import prelude.ls in REPL |
-r, --require | require libraries before executing |
-s, --stdin | read stdin |
-j, --json | print/compile as JSON |
-n, --nodejs | pass options after this through to the 'node' binary |
-w, --watch | watch scripts for changes, and repeat |
-k, --const | compile all variables as constants |
Output control:
-o, --output path::String | compile into the specified directory |
-p, --print | print the result to stdout |
-b, --bare | compile without the top-level function wrapper |
--header | add "Generated by" header, '--no-header' to suppress - default: true |
-l, --lex | print the tokens the lexer produces |
-t, --tokens | print the tokens the rewriter produces |
-a, --ast | print the syntax tree the parser produces |
Command Line Examples
- Run a LiveScript file (through node.js):
lsc file.ls
- you can omit the.ls
if you wish. - Compile a LiveScript file, creating a
file.js
:lsc -c file.ls
- Watch a file and compile on changes:
lsc -wc file.ls
- Compile a directory and output to another directory:
lsc -co output src
- Watch a directory, compile on change into an output directory:
lsc -wco output src
- Compile a one-liner and print the JS:
lsc -bpe '[1 to 5]'
- Start the LiveScript REPL:
lsc
-Ctrl-D
to exit, useCtrl-J
for multiline input.
Programmatic API:
Require it into your Node or Browserify project with
var LiveScript =
require('LiveScript');
or require! LiveScript
, or use the
installation steps above for in-browser installation. In Node,
requiring this also registers it in
require.extensions
.
General:
LiveScript.compile(code :: String, options :: Object?)
-> String
Compile a string of LiveScript code into plain JavaScript. If the string fails to compile, a SyntaxError is thrown.
Options:
bare :: Boolean = false | if true, compile without the top-level function wrapper |
header :: Boolean = true | if true, add the "Generated by" header |
const :: Boolean = false | if true, compile all variables as constants |
filename :: String? | an optional filename to use for compilation errors |
LiveScript.run(code :: String, options :: Object?) ->
String
Evaluate a string of LiveScript code. If the string fails to compile, a SyntaxError is thrown. Note that this uses the Function constructor.
Options:
const :: Boolean = false | if true, compile all variables as constants |
filename :: String? | an optional filename to use for errors |
Utilities:
LiveScript.ast(code :: String|Array) ->
Object
Generate the AST representation of the LiveScript source, if it is a string, or the token stream if it is an array. If it is a string that cannot be parsed as LiveScript code, a SyntaxError is thrown. If it is an Array, an Error is thrown if the stream is not valid.
LiveScript.tokens(code :: String, options :: Object?)
-> Array
Generate a token stream from LiveScript code. Note that this does keep state between calls.
Options
raw :: Boolean = false | if true, do not flush the token stream before tokenizing - recommended to leave as default |
line :: Number = 0 | the starting line number for the token stream |
LiveScript.lex(code :: String)
Equivalent to LiveScript.tokens(code, {raw:
true})
.
LiveScript.ast.* :: Object...
All the AST constructors used for
LiveScript.ast()
.
Browser-specific methods:
LiveScript.stab(code :: String, callback :: (err ::
Error?) -> void, filename :: String?) -> void
Run a string of code, and call back with an optional error.
LiveScript.load(url :: String, callback :: (err :: Error?)
-> void) -> void
Load a remote LiveScript file via an XMLHttpRequest
at url
and call back with an optional error.
LiveScript.go() -> void
Load all scripts with a type
attribute of either
"text/ls"
or "application/ls"
.
Community
- LiveScript GitHub issues section
- Google Groups
- r/livescript
- Find us at #livescript on freenode.
- Follow the LiveScript style guide
Blog
- LiveScript 1.3.0 Released!
- LiveScript 1.2.0 Released!
- 10 things you didn't know LiveScript can do
- The shortest FizzBuzzBazz - can you do better?
- LiveScript 1.1.0 Released!
- Powerful jQuery with LiveScript
- LiveScript 1.0.0 Released
- Functional Programming in JavaScript using LiveScript - Part 2
- 10 Reasons to Switch from CoffeeScript to LiveScript
- Functional Programming in JavaScript using LiveScript and prelude.ls
- 10 LiveScript One Liners to Impress Your Friends
In the Wild
Add your own project/company to the lists!
Text Editor/IDE Plugins
- Vim users, check out vim-ls.
- TextMate, Chocolat, and Sublime Text bundle for LiveScript: LiveScript.tmbundle.
- Emacs basic LiveScript support: livescript-mode.
- And more on the wiki page. Add yours to the growing list!
Standard Library
prelude.ls is the recommended base library when using LiveScript. It allows you to do things such as:[1 2 3] |> map (* 2) |> filter (> 3) |> fold1 (+) #=> 10
You can automatically import prelude.ls to the REPL by using the
-d
or --prelude
options.
Prelude is loaded on this page and you can use it when running things in the compiler/REPL to the right.
Introduction
Like many modern languages, blocks are delimited by whitespace indentation and newlines are used instead of semicolons to terminate statements (you can still use semicolons if you want to fit more than one statement on a line).
For example (LiveScript on the left, compiled JavaScript on the right):
if 2 + 2 == 4 do-something()
if (2 + 2 === 4) { doSomething(); }
You can try all these examples for yourself using the LiveScript compiler/REPL to the right.
To further clear things up, you can omit the parentheses when calling a function.
add 2, 3
add(2, 3);
And comments are:
# from here to the end of the line.
// from here to the end of the line.
Lisp hackers, you may be pleased to know that you
can use dashes in the name of your variables and functions. The
names are equivalent to, and are compiled to, camel case. Eg.
my-value = 42
== myValue = 42
.
The file extension for LiveScript is .ls
.
Defining Functions
Defining functions is very lightweight in LiveScript:
(x, y) -> x + y -> # an empty function times = (x, y) -> x * y # multiple lines, and be assigned to # a var like in JavaScript
var times; (function(x, y){ return x + y; }); (function(){}); times = function(x, y){ return x * y; };
As you see, function definitions are considerably shorter! You
may also have noticed that we have omitted return
. In
LiveScript, almost everything is an expression and the last one
reached is automatically returned. However, you can still use
return
to force returns if you want, and you can add a
bang !
before the arrow to suppress auto-returning
no-ret = (x) !-> ...
.
Assignment
Basic assignment is as you would expect, variable =
value
, and there is no need for variable declarations.
However, unlike CoffeeScript, you must use :=
to
modify variables in upper scopes.
x = 10 do -> x = 5 x #=> 10 do -> x := 2 x #=> 2
var x; x = 10; (function(){ var x; return x = 5; })(); x; (function(){ return x = 2; })(); x;
Almost everything is an expression, which means you can do things like:
x = if 2 + 2 == 4 then 10 else 0 x #=> 10
var x; x = 2 + 2 === 4 ? 10 : 0; x;
Things such as loops, switch statements, and even try/catch statements are all expressions.
If you want to simply declare a variable and not initialize it,
you can use var
.
var x
var x;
You can also declare constants in LiveScript using
const
. They are checked at compile time - the compiled
JS is no different.
Attempting to compile the following:
const x = 10 x = 0
Results in redeclaration of constant "x" on line
2
.
However, objects are not frozen if declared as constants - you
can still modify their properties. You can force all variables to
be constants if you compile with the -k
or
--const
flags.
Info
For differences from CoffeeScript, see the CoffeeScript to LiveScript Conversion Guide.
You can double click any of the examples to load the LiveScript
code into the compiler to the right, or you can play around and try
out your own code. Press run to execute the compiled JavaScript.
Note that LiveScript wraps compiled JS in a safety wrapper
(function(){...contents...}).call(this);
- this
wrapper has been omitted in all examples and this page's compiler
output for conciseness.
Literals
Numbers
.4
is not valid, it must be preceded with a zero,
eg. 0.4
.
42 17.34 0.4
42; 17.34; 0.4;
Underscores and appended letters are ignored.
64_000km
64000;
Any base can be used from 2 to 36 using ~
.
6~12 2~1000 16~ff
8; 8; 255;
Booleans, Void, Null
Aliases as in CoffeeScript.
true false on off yes no
true; false; true; false; true; false;
In JavaScript, undefined
can be redefined, so it is
prudent to use the void
operator which produces the
undefined value, always.
void
at the top level (not used as an expression)
compiles to nothing (for use as a placeholder) - it must be used as
a value to compile.
void x = void null
var x; // void compiles to nothing here! x = void 8; null;
Strings
You can use double or single quotes.
'a string' "a string"
'a string'; "a string";
Strings can be written with a preceding backslash instead of
quotes. Backslash strings can't contain , ; ] ) }
or
whitespace.
\word func \word, \word; (func \word) [\word] {prop:\word}
'word'; func('word', 'word'); func('word'); ['word']; ({ prop: 'word' });
Double quoted strings allow interpolation. Single quoted strings are passed through as-is. Simple variables can be interpolated without curly braces.
"The answer is #{2 + 2}" 'As #{is}' variable = "world" "Hello #variable"
var variable; "The answer is " + (2 + 2); 'As #{is}'; variable = "world"; "Hello " + variable;
Prefixing your interpolated string with %
returns
and the raw parts as an array. This allows you to join the result
as you wish.
%"#x #y"
[x, " ", y];
Multiline strings (can also do the same but with double quotes for use with interpolation):
multiline = 'string can be multiline and go on and on beginning whitespace is ignored' heredoc = ''' string can be multiline with newlines and go on and on beginning whitespace is ignored '''
var multiline, heredoc; multiline = 'string can be multiline and go on and on beginning whitespace is ignored'; heredoc = 'string can be multiline\nwith newlines\nand go on and on\nbeginning whitespace is\nignored';
Comments
Single line comments start off with a #
. They are
not passed through to the compiled output.
# single line comment
Multiline comments are preserved in the output.
/* multiline comments use this format and are preserved in the output unlike single line ones */
/* multiline comments use this format and are preserved in the output unlike single line ones */
Objects
Braces are optional:
obj = {prop: 1, thing: 'moo'} person = age: 23 eye-color: 'green' height: 180cm oneline = color: 'blue', heat: 4
var obj, person, oneline; obj = { prop: 1, thing: 'moo' }; person = { age: 23, eyeColor: 'green', height: 180 }; oneline = { color: 'blue', heat: 4 };
Dynamic keys:
obj = "#variable": 234 (person.eye-color): false
var ref$, obj; obj = (ref$ = {}, ref$[variable + ""] = 234, ref$[person.eyeColor] = false, ref$);
Property setting shorthand - easily set properties with variables if you want the property name to be the same as the variable name.
x = 1 y = 2 obj = {x, y}
var x, y, obj; x = 1; y = 2; obj = { x: x, y: y };
Flagging shorthand - easily set boolean properties.
{+debug, -live}
({ debug: true, live: false });
This - no need to use a dot .
to access
properties.
this @ @location
this; this; this.location;
Regular Expressions
Regular regex delineated with a single /
.
/moo/gi
/moo/gi;
Delineated with //
- multiline, comments,
spacing!
// | [!=]==? # equality | @@ # constructor | <\[(?:[\s\S]*?\]>)? # words //g
/|[!=]==?|@@|<\[(?:[\s\S]*?\]>)?/g;
Lists
Regular list literal delineated with brackets:
[1, person.age, 'French Fries']
[1, person.age, 'French Fries'];
Commas are not needed if the item preceding is not callable:
[1 2 3 true void \word 'hello there']
[1, 2, 3, true, void 8, 'word', 'hello there'];
Implicit lists created with an indented block. They need at
least two items for it to work. If you have only one item, you can
add a yaddayaddayadda ...
to force the implicit
list.
my-list = 32 + 1 person.height 'beautiful' one-item = 1 ...
var myList, oneItem; myList = [32 + 1, person.height, 'beautiful']; oneItem = [1];
When implicitly listing, you can use an asterisk *
to disambiguate implicit structures such as implicit objects and
implicit lists. The asterisk does not denote an item of the list,
but merely sets aside an implicit structure so that it is not
muddled with the other ones being listed.
tree = * 1 * 2 3 4 * 5 6 * 7 8 * 9 10 11 obj-list = * name: 'tessa' age: 23 * name: 'kendall' age: 19 obj = * name: 'tessa' age: 23 obj-one-list = * name: 'tessa' age: 23 ...
var tree, objList, obj, objOneList; tree = [[1, [2, 3], 4], [5, 6, [7, 8, [9, 10]], 11]]; objList = [ { name: 'tessa', age: 23 }, { name: 'kendall', age: 19 } ]; obj = { name: 'tessa', age: 23 }; objOneList = [{ name: 'tessa', age: 23 }];
Lists of words:
<[ list of words ]>
['list', 'of', 'words'];
Ranges
to
means up to and including the number.
til
means up until but not including the
number.
You can optionally add a by
which defines the step
of the range.
If you omit the first number, it is assumed to be
0
.
With number/string literals:
[1 to 5] #=> [1, 2, 3, 4, 5] [1 til 5] #=> [1, 2, 3, 4] [1 to 10 by 2] #=> [1, 3, 7, 9] [4 to 1] #=> [4, 3, 2, 1] [to 5] #=> [0, 1, 2, 3, 4, 5] [\A to \D] #=> ['A', 'B', 'C', D']
var i$; [1, 2, 3, 4, 5]; [1, 2, 3, 4]; [1, 3, 5, 7, 9]; [4, 3, 2, 1]; [0, 1, 2, 3, 4, 5]; ["A", "B", "C", "D"];
With any expression - if your range uses expressions, and you
want it to go downwards (ie. from a larger number to a smaller one)
you must explicitly set by -1
.
x = 4 [1 to x] #=> [1, 2, 3, 4] [x to 0 by -1] #=> [4, 3, 2, 1, 0]
var x, i$; x = 4; for (i$ = 1; i$ <= x; ++i$) { i$; } for (i$ = x; i$ >= 0; --i$) { i$; }
Misc
Labels (useful for nested loops):
:label 4 + 2
label: { 4 + 2; }
constructor
shorthand.
@@ @@x x@@y
constructor; constructor.x; x.constructor.y;
Yaddayaddayadda - a placeholder:
...
throw Error('unimplemented');
Operators
Number
The standard math operators:
1 + 2 #=> 3 3 - 4 #=> -1 6 * 2 #=> 12 8 / 4 #=> 2
1 + 2; 3 - 4; 6 * 2; 8 / 4;
There is a remainder operator, as in JavaScript - but we also add a proper modulo operator.
-3 % 4 #=> -3 -3 %% 4 #=> 1
var ref$; -3 % 4; ((-3) % (ref$ = 4) + ref$) % ref$;
The power is right associative, and has higher precedence than
unary ops. ^
is an alias for **
2 ** 4 #=> 16 3 ^ 4 #=> 81 -2 ^ 2 ^ 3 #=> -256
Math.pow(2, 4); Math.pow(3, 4); -Math.pow(2, Math.pow(2, 3));
Increments and decrements:
n = 0 n++ #=> 0 ++n #=> 2 n-- #=> 2 --n #=> 0 x = n++ #=> 0 x #=> 0 n #=> 1 x = ++n #=> 2 x #=> 2 n #=> 2
var n, x; n = 0; n++; ++n; n--; --n; x = n++; x; n; x = ++n; x; n;
Bitwise and shift operators:
14 .&. 9 #=> 8 14 .|. 9 #=> 15 14 .^. 9 #=> 7 ~9 #=> -10 9 .<<. 2 #=> 36 -9 .>>. 2 #=> -3 -9 .>>>. 2 #=> 1073741821
14 & 9; 14 | 9; 14 ^ 9; ~9; 9 << 2; -9 >> 2; -9 >>> 2;
Casting to a number:
+'4' #=> 4 -'3' #=> -3
+'4'; -'3';
Comparison
Strict equality (no type coercion):
2 + 4 == 6 #=> true \boom is 'boom' #=> true \boom != null #=> true 2 + 2 is not 4 #=> false 0 + 1 isnt 1 #=> false
2 + 4 === 6; 'boom' === 'boom'; 'boom' !== null; 2 + 2 !== 4; 0 + 1 !== 1;
Fuzzy equality (with type coercion):
2 ~= '2' #=> true \1 !~= 1 #=> false
2 == '2'; '1' != 1;
Greater/less than:
2 < 4 #=> true 9 > 7 #=> true 8 <= 8 #=> true 7 >= 8 #=> false
2 < 4; 9 > 7; 8 <= 8; 7 >= 8;
Chained comparison:
1 < 2 < 4 #=> true 1 < 2 == 4/2 > 0 #=> true
var ref$; 1 < 2 && 2 < 4; 1 < 2 && 2 === (ref$ = 4 / 2) && ref$ > 0;
Minimum/maximum - returns the smaller/larger of the two operands.
4 >? 8 #=> 8 9 - 5 <? 6 #=> 4
var ref$; 4 > 8 ? 4 : 8; (ref$ = 9 - 5) < 6 ? ref$ : 6;
When one of the operands of a equals (==
or
is
, and the negatives for that) is a regex literal it
will test the other operand against it. Equality compiles to
exec
so you can use the results, while the negative
simply compiles to test
.
/^e(.*)/ is 'enter' #=> ["enter","nter"] /^e(.*)/ == 'zx' #=> null /moo/ != 'loo' #=> true
/^e(.*)/.exec('enter'); /^e(.*)/.exec('zx'); !/moo/.test('loo');
Logic
The basics:
true and false #=> false true && false #=> false true or false #=> true true || false #=> true not false #=> true !false #=> true
true && false; true && false; true || false; true || false; !false; !false;
A logical operator not commonly seen in other languages - exclusive or:
false xor true #=> true false xor false #=> false 1 xor 0 #=> 1 1 xor 1 #=> false
((true, false) || true) && !(false && true) && (false || true); ((false, false) || false) && !(false && false) && (false || false); ((0, 1) || 0) && !(1 && 0) && (1 || 0); ((1, 1) || 1) && !(1 && 1) && (1 || 1);
and
, or
, and xor
close
implicit calls, while ||
and &&
do not.
even 0 and 3 #=> 3 even 0 && 3 #=> true
even(0) && 3; even(0 && 3);
You can call logic operators.
(f or g) 1 (f and g or h) 3 4
f(1) || g(1); f(3, 4) && g(3, 4) || h(3, 4);
In/Of
Use in
to check if and element is in a list; use
of
to check if a key is in an object.
list = [7 8 9] 2 in [1 2 3 4 5] #=> true 3 in list #=> false \id of id: 23, name: \rogers #=> true
var list; list = [7, 8, 9]; 2 == 1 || 2 == 2 || 2 == 3 || 2 == 4 || 2 == 5; in$(3, list); 'id' in { id: 23, name: 'rogers' }; function in$(x, arr){ var i = 0, l = arr.length >>> 0; while (i < l) if (x === arr[i++]) return true; return false; }
Piping
Instead of a series of nested function calls, you can pipe
values in. x |> f
and f <| x
are
equivalent to f(x)
.
x = [1 2 3] |> reverse |> head #=> 3 y = reverse <| [1 2 3] #=> [3,2,1]
var x, y; x = head( reverse( [1, 2, 3])); y = reverse([1, 2, 3]);
You can use newlines to space things out better.
4 |> (+ 1) |> even #=> false
even( (function(it){ return it + 1; })( 4));
Function
Composing allows you to create functions by composing
them out of a series of functions. LiveScript has two operators for
composing, forward >>
and backwards
<<
.
(f << g) x
is equivalent to
f(g(x))
, and (f >> g) x
is
equivalent to g(f(x))
. For example:
odd = (not) << even odd 3 #=> true
var odd; odd = function(){ return not$(even.apply(this, arguments)); }; odd(3); function not$(x){ return !x; }
To be a bit more clear about the difference between the two operators:
add-two-times-two = (+ 2) >> (* 2) times-two-add-two = (+ 2) << (* 2) add-two-times-two 3 #=> (3+2)*2 => 10 times-two-add-two 3 #=> (3*2)+2 => 8
var addTwoTimesTwo, timesTwoAddTwo; addTwoTimesTwo = function(){ return (function(it){ return it * 2; })((function(it){ return it + 2; }).apply(this, arguments)); }; timesTwoAddTwo = function(){ return (function(it){ return it + 2; })((function(it){ return it * 2; }).apply(this, arguments)); }; addTwoTimesTwo(3); timesTwoAddTwo(3);
You can use a spaced dot as an alias to <<
,
for example f . g
, just as in Haskell.
List
You can concatenate two lists together:
<[ one two three ]> ++ [\four] #=> ['one','two','three','four']
['one', 'two', 'three'].concat(['four']);
Note that the concat operator must either be spaced on both
sides xs ++ ys
, or not spaced on both sides
xs++ys
. If it is spaced on one side only, then it is
taken to be the increment operator.
List repetition when first is list literal:
[\ha] * 3 #=> ['ha','ha','ha']
['ha', 'ha', 'ha'];
Join when the right operand is a string literal:
<[ one two three ]> * \| #=> 'one|two|three'
['one', 'two', 'three'].join('|');
Unary spread - when the operand is a list literal, apply the
unary op to each item:
r = +[4 5 6] #=> [+4, +5, +6] t = typeof! [\b 5 {}] #=> ["String", "Number", "Object"] c = ~[4, 5] #=> [-5, -6] ++player<[strength hp]> # also works with -, --, typeof, ! and delete! i = new [some, classes] c = ^^[copy, these, {}] delete list[1, 2, 3] do [a, b, c]
var r, t, c, i, toString$ = {}.toString; r = [+4, +5, +6]; t = [toString$.call('b').slice(8, -1), toString$.call(5).slice(8, -1), toString$.call({}).slice(8, -1)]; c = [~4, ~5]; ++player['strength'], ++player['hp']; i = [new some, new classes]; c = [clone$(copy), clone$(these), clone$({})]; delete list[1], delete list[2], delete list[3]; a(), b(), c(); function clone$(it){ function fun(){} fun.prototype = it; return new fun; }
String
String concatenation:
'hello' + ' ' + 'world' #=> 'hello world' string = 'say ' #=> 'say ' string += \yeah #=> 'say yeah'
var string; 'hello' + ' ' + 'world'; string = 'say '; string += 'yeah';
String repetition when the first operand is a string literal:
'X' * 3 #=> 'XXX'
'XXX';
String subtraction/division when the right operand is a string
or regex literal - subtraction means replace
, division
is split
.
'say yeah' - /h/ #=> 'say yea' 'say yeah' / \y #=> ['sa',' ','eah']
'say yeah'.replace(/h/, ''); 'say yeah'.split('y');
Existence
The ?
operator can be used in a variety of contexts
to check for existence.
bigfoot ? 'grizzly bear' #=> 'grizzly bear' string = \boom if window? #=> 'boom' document?.host #=> 'gkz.github.com'
var string; (typeof bigfoot == 'undefined' || bigfoot === null) && 'grizzly bear'; if (typeof window != 'undefined' && window !== null) { string = 'boom'; } if (typeof document != 'undefined' && document !== null) { document.host; }
Object
Instanceof - list literals to the right get expanded:
new Date() instanceof Date #=> true new Date() instanceof [Date, Object] #=> true
var ref$; new Date() instanceof Date; (ref$ = new Date()) instanceof Date || ref$ instanceof Object;
Typeof - add a bang for a useful alternative:
typeof /^/ #=> object typeof! /^/ #=> RegExp
var toString$ = {}.toString; typeof /^/; toString$.call(/^/).slice(8, -1);
Delete returns the value of the deleted item:
obj = {one: 1, two: 2} r = delete obj.one r #=> 1
var obj, r, ref$; obj = { one: 1, two: 2 }; r = (ref$ = obj.one, delete obj.one, ref$); r;
delete!
is like delete
in JavaScript,
and returns false only if the property exists and can't be deleted,
otherwise returns true.
obj = {one: 1, two: 2} delete! obj.one #=> true delete! Math.PI #=> false
var obj; obj = { one: 1, two: 2 }; delete obj.one; delete Math.PI;
Property copy - copy enumerable properties from right to left,
and evaluate to the left. <<<
for own
properties, <<<<
for all properties.
import
and import all
are aliases for the
two respectively with the exception that if you leave out the left
operand, this
is presumed.
obj = {one: 1, two: 2} obj <<< three: 3 #=> {one: 1, two: 2, three: 3} {go: true} <<<< window import obj
var obj; obj = { one: 1, two: 2 }; obj.three = 3; importAll$({ go: true }, window); import$(this, obj); function importAll$(obj, src){ for (var key in src) obj[key] = src[key]; return obj; } function import$(obj, src){ var own = {}.hasOwnProperty; for (var key in src) if (own.call(src, key)) obj[key] = src[key]; return obj; }
Clone - creates a prototypical clone of the operand. It does not create a deep clone of the object, rather the resulting object's prototype is the operand. Remember that prototypes are ignored when serializing to JSON.
obj = {one: 1} obj2 = ^^obj obj2.two = 2 obj2 #=> {one: 1, two: 2} # above includes its prototype's properties # JSON serialization would be just `{two: 2}` obj #=> {one: 1}
var obj, obj2; obj = {one: 1}; obj2 = clone$(obj); obj2.two = 2; obj2; obj function clone$(it){ function fun(){} fun.prototype = it; return new fun; }
The infix with (aka the cloneport) combines
the clone and property copy operators for easy object creation. It
is equivalent to ^^obj <<< obj2
. Remember
that the clone operator creates a prototypical clone, and
prototypes are not serialized in JSON.
girl = {name: \hanna, age: 22} guy = girl with name: \john guy #=> {name: 'john', age: 22} # the above result include the object's prototype # in the result - the actual JSON: {name: 'john'} girl #=> {name: 'hanna', age: 22}
var girl, ref$, guy; girl = { name: 'hanna', age: 22 }; guy = (ref$ = clone$(girl), ref$.name = 'john', ref$); guy; guy.age; function clone$(it){ function fun(){} fun.prototype = it; return new fun; }
Partial Application, Operators as Functions
You can partially apply operators and use them as functions
(+ 2) 4 #=> 6 (*) 4 3 #=> 12 (not) true #=> false (in [1 to 3]) 2 #=> true
(function(it){ return it + 2; })(4); curry$(function(x$, y$){ return x$ * y$; })(4, 3); not$(true); (function(it){ return it == 1 || it == 2 || it == 3; })(2); function curry$(f, bound){ var context, _curry = function(args) { return f.length > 1 ? function(){ var params = args ? args.concat() : []; context = bound ? context || this : this; return params.push.apply(params, arguments) < f.length && arguments.length ? _curry.call(context, params) : f.apply(context, params); } : f; }; return _curry(); } function not$(x){ return !x; }
export
By using the export
operator instead of
exports
you get a more concise way to define
modules.
export func = -> export value export value-a, value-b, value-c export a: 1 b: -> 123 export class MyClass
var func, ref$, MyClass, out$ = typeof exports != 'undefined' && exports || this; out$.func = func = function(){}; out$.value = value; out$.valueA = valueA; out$.valueB = valueB; out$.valueC = valueC; ref$ = out$; ref$.a = 1; ref$.b = function(){ return 123; }; out$.MyClass = MyClass = (function(){ MyClass.displayName = 'MyClass'; var prototype = MyClass.prototype, constructor = MyClass; function MyClass(){} return MyClass; }());
require!
Having to require a series of modules results in a lot of cruft.
You can get rid of that cruft by using require!
, which
takes an ID, or a string, array, or object literal.
If you are requiring a module with dashes in its name, you must use a string literal.
You can rename what you are requiring by using an object literal.
You can destructure to get the contents of value.
require! lib require! 'lib1' require! prelude-ls # no require! 'prelude-ls' require! [fs, path] require! <[ fs path ]> require! jQuery: $ require! { fs path lib: foo }
var lib, lib1, preludeLs, fs, path, $, foo; lib = require('lib'); lib1 = require('lib1'); preludeLs = require('preludeLs'); preludeLs = require('prelude-ls'); fs = require('fs'); path = require('path'); fs = require('fs'); path = require('path'); $ = require('jQuery'); fs = require('fs'); path = require('path'); foo = require('lib');
You can easily require parts of modules with destructuring.
require! { fs: filesystem 'prelude-ls': {map, id} path: {join, resolve}:p }
var filesystem, ref$, map, id, p, join, resolve; filesystem = require('fs'); ref$ = require('prelude-ls'), map = ref$.map, id = ref$.id; p = require('path'), join = p.join, resolve = p.resolve;
Filenames are automatically extracted.
require! 'lib.js' require! './dir/lib1.js'
var lib, lib1; lib = require('lib.js'); lib1 = require('./dir/lib1.js');
Functions
Defining functions is very lightweight in LiveScript:
(x, y) -> x + y -> # an empty function times = (x, y) -> x * y # multiple lines, and be assigned to # a var like in JavaScript
var times; (function(x, y){ return x + y; }); (function(){}); times = function(x, y){ return x * y; };
As you see, function definitions are considerably shorter! You
may also have noticed that we have omitted return
. In
LiveScript, almost everything is an expression and the last one
reached is automatically returned.
You can prepend the function arrow with a bang !
to
suppress automatic returning.
f = !-> 2 g = (x) !-> x + 2
var f, g; f = function(){ 2; }; g = function(x){ x + 2; };
Calling
You can omit the parentheses when calling a function, and you can omit the comma separating the arguments if the preceding item is not callable, just like in arrays.
x = 4 Math.pow x, 3 #=> 64 Math.pow 2 3 #=> 8
var x; x = 4; Math.pow(x, 3); Math.pow(2, 3);
If you are calling the function with no arguments, you can use a
bang !
- as well you don't need to use a dot when
chaining banged functions.
f! [1 2 3].reverse!slice 1 #=> [2,1]
f(); [1, 2, 3].reverse().slice(1);
and
, or
, xor
, spaced
.
or ?.
all close implicit calls -
allowing for parentheses free chaining.
$ \h1 .find \a .text! #=> LiveScript
$('h1').find('a').text();
You can use do
to call functions with no
arguments:
do -> 3 + 2 #=> 5
(function(){ return 3 + 2; })();
If you use do
on a named function, when the
do
is not used as an expression, the named function
will remain a function statement.
i = 0 f 9 #=> 9 i #=> 1 do function f x ++i x i #=> 2
var i; i = 0; f(9); i; function f(x){ ++i; return x; } f(); i;
You can't call a function with an implicit object, if you want
to do that you can use do
:
func do a: 1 b: 2
func({ a: 1, b: 2 });
do
allows you to do many things without adding
extra parentheses.
pow do 1 2 h 1 do a: 2 b: 5
pow(1, 2); h(1, { a: 2, b: 5 });
You can also call functions infix using backticks
`
.
add = (x, y) -> x + y 3 `add` 4 #=> 7
var add; add = function(x, y){ return x + y; }; add(3, 4);
Calling a function with bare splats ...
implies
calling it with the arguments of the current function. Especially
useful when calling super
.
f = (x, y) -> x + y g = (a, b) -> f ... g 3 4 #=> 7
var f, g; f = function(x, y){ return x + y; }; g = function(a, b){ return f.apply(this, arguments); }; g(3, 4);
Parameters
Extended parameters:
set-person-params = ( person # target object to set params person.age person.height ) -> person person = set-person-params {}, 21, 180cm #=> {age: 21, height: 180}
var setPersonParams, person; setPersonParams = function(person, age, height){ person.age = age; person.height = height; return person; }; person = setPersonParams({}, 21, 180);
This is especially useful with this
.
set-text = (@text) -> this
var setText; setText = function(text){ this.text = text; return this; };
You can set default arguments:
add = (x = 4, y = 3) -> x + y add 1 2 #=> 3 add 1 #=> 4 add! #=> 7
var add; add = function(x, y){ x == null && (x = 4); y == null && (y = 3); return x + y; }; add(1, 2);
...or indeed use any logical operator (in parameters, x =
2
is just sugar for doing x ? 2
):
add = (x && 4, y || 3) -> x + y add 1 2 #=> 6 add 2 0 #=> 7
var add; add = function(x, y){ x && (x = 4); y || (y = 3); return x + y; }; add(1, 2); add(2, 0);
You can also destructure the arguments:
set-cords = ({x, y}) -> "#x,#y" set-cords y: 2, x: 3 #=> '3,2'
var setCords; setCords = function(arg$){ var x, y; x = arg$.x, y = arg$.y; return x + "," + y; }; setCords({ y: 2, x: 3 });
...and even set defaults (or use any logic) on those destructured parameters, functioning like Python's keyword arguments.
set-cords = ({x = 1, y = 3} = {}) -> "#x,#y" set-cords y: 2, x: 3 #=> '3,2' set-cords x: 2 #=> '2,3' set-cords y: 7 #=> '1,7' set-cords! #=> '1,3'
var setCords; setCords = function(arg$){ var ref$, ref1$, x, y; ref$ = arg$ != null ? arg$ : {}, x = (ref1$ = ref$.x) != null ? ref1$ : 1, y = (ref1$ = ref$.y) != null ? ref1$ : 3; return x + "," + y; }; setCords({ y: 2, x: 3 }); setCords({ x: 2 }); setCords({ y: 7 }); setCords();
You can also use splats in your parameters:
f = (x, ...ys) -> x + ys.1 f 1 2 3 4 #=> 4
var f, slice$ = [].slice; f = function(x){ var ys; ys = slice$.call(arguments, 1); return x + ys[1]; }; f(1, 2, 3, 4);
You can even use unary operators in your parameters. You could
use +
and !!
to cast your parameters to a
number or boolean respectively, or use the clone operator
^^
to make sure any changes you make to the object are
not reflected in the original. You can still use extended
parameters, eg. (!!x.x) ->
.
f = (!!x) -> x f 'truthy string' #=> true g = (+x) -> x g '' #=> 0 obj = {prop: 1} h = (^^x) -> x.prop = 99 x h obj obj.prop #=> 1
var f, g, obj, h; f = function(x){ x = !!x; return x; }; f('truthy string'); g = function(x){ x = +x; return x; }; g(''); obj = { prop: 1 }; h = function(x){ x = clone$(x); x.prop = 99; return x; }; h(obj); obj.prop; function clone$(it){ function fun(){} fun.prototype = it; return new fun; }
Currying
Curried functions are very powerful. Essentially, when called with less arguments than defined with, they return a partially applied function. This means that it returns a function whose arguments are those which you didn't supply, with the values for what you did supply already bound. They are defined in LiveScript using the long arrow. Perhaps an example will make things more clear:
times = (x, y) --> x * y times 2, 3 #=> 6 (normal use works as expected) double = times 2 double 5 #=> 10
var times, double; times = curry$(function(x, y){ return x * y; }); times(2, 3); double = times(2); double(5); function curry$(f, bound){ var context, _curry = function(args) { return f.length > 1 ? function(){ var params = args ? args.concat() : []; context = bound ? context || this : this; return params.push.apply(params, arguments) < f.length && arguments.length ? _curry.call(context, params) : f.apply(context, params); } : f; }; return _curry(); }
You can define bound curried functions with a long wavy arrow:
~~>
If you call a curried function with no arguments, it will evaluate as is, allowing you to use default arguments.
f = (x = 5, y = 10) --> x + y f! #=> 15 g = f 20 g 7 #=> 27 g! #=> 30
var f, g; f = curry$(function(x, y){ x == null && (x = 5); y == null && (y = 10); return x + y; }); f(); g = f(20); g(7); g(); function curry$(f, bound){ var context, _curry = function(args) { return f.length > 1 ? function(){ var params = args ? args.concat() : []; context = bound ? context || this : this; return params.push.apply(params, arguments) < f.length && arguments.length ? _curry.call(context, params) : f.apply(context, params); } : f; }; return _curry(); }
Named Functions
You can create named functions whose definition is hoisted to the top of the scope - this is useful for defining utility functions at the end of the file instead of the top. Name functions are constants, and can't be redefined.
util! #=> 'available above declaration' util2! #=> 2 function util 'available above declaration' function util2 then 2
util(); util2(); function util(){ return 'available above declaration'; } function util2(){ return 2; }
You can prepend the function definition with a ~
to
make it a bound function.
~function add x, y @result = x + y
var this$ = this; function add(x, y){ return this$.result = x + y; }
You can prepend it with a bang !
to suppress
returning.
util! #=> nothing !function util x then x
util(); function util(x){ x; }
You can combine the ~
and !
to make a
bound non-returning function if you wish.
Bound Functions
Defined using the wavy arrow ~>
. Use the long
wavy arrow for curried and bound functions ~~>
.
Prepend a ~
to named functions to make them bound.
Bound functions have this
lexically bound, not
dynamically bound as normally. This means that it does not matter
in which context they are called, the value of this
in
their body will always be the value of this
where they
were defined.
obj = new @x = 10 @normal = -> @x @bound = ~> @x obj2 = x: 5 obj2.normal = obj.normal obj2.bound = obj.bound obj2.normal! #=> 5 obj2.bound! #=> 10
var obj, obj2; obj = new function(){ var this$ = this; this.x = 10; this.normal = function(){ return this.x; }; this.bound = function(){ return this$.x; }; }; obj2 = { x: 5 }; obj2.normal = obj.normal; obj2.bound = obj.bound; obj2.normal(); obj2.bound();
Check out the OOP section for more on bound functions when used in classes.
Let, New
let
is short for (function(a){...}.call(this,
b))
.
let $ = jQuery $.isArray [] #=> true
(function($){ $.isArray([]); }.call(this, jQuery));
You can also define this
(aka @
) with
let
.
x = let @ = a: 1, b: 2 @b ^ 3 x #=> 8
var x; x = (function(){ return Math.pow(this.b, 3); }.call({ a: 1, b: 2 })); x;
With new context:
dog = new @name = \spot @mutt = true #=> {name: 'spot', mutt: true}
var dog; dog = new function(){ this.name = 'spot'; this.mutt = true; };
Access/Call Function Shorthand
There are especially useful for higher order functions like map and filter.
(.prop)
is short for (it) ->
it.prop
.
map (.length), <[ hello there you ]> #=> [5,5,3] filter (.length < 4), <[ hello there you ]> #=> ['you']
map(function(it){ return it.length; }, ['hello', 'there', 'you']); filter(function(it){ return it.length < 4; }, ['hello', 'there', 'you']);
You can also use this to call methods:
map (.join \|), [[1 2 3], [7 8 9]] #=> ['1|2|3','7|8|9']
map(function(it){ return it.join('|'); }, [[1, 2, 3], [7, 8, 9]]);
(obj.)
is short for (it) ->
obj[it]
.
obj = one: 1, two: 2, three: 3 map (obj.), <[ one three ]> #=> [1,3]
var obj; obj = { one: 1, two: 2, three: 3 }; map(function(it){ return obj[it]; }, ['one', 'three']);
Backcalls
Backcalls are very useful. They allow you to unnest callbacks.
They are defined using arrows pointed to the left. All the syntax
is the same as regular arrows for defining bound functions
(<~
), curried functions (<--,
<~~
), suppressing return (<-!
) - except
that it is just pointing the other way.
<- $ alert \boom
$(function(){ return alert('boom'); });
They can take arguments, and you can specify a placeholder for where you want it to go.
x <- map _, [1 to 3] x * 2 #=> [2, 4, 6]
map(function(x){ return x * 2; }, [1, 2, 3]);
If you wish to have further code after your backcalls, you can
set them aside with a do
statement.
do data <-! $.get 'ajaxtest' $ '.result' .html data processed <-! $.get 'ajaxprocess', data $ '.result' .append processed alert 'hi'
$.get('ajaxtest', function(data){ $('.result').html(data); $.get('ajaxprocess', data, function(processed){ $('.result').append(processed); }); }); alert('hi');
If you want further async functionality, you can use async.js with LiveScript.
Partial Application
You can partially apply functions using the underscore
_
as a placeholder. Sometimes, the function you want
to deal with isn't curried, or if it is the arguments are not in a
good order. In these cases partial application is very useful.
filter-nums = filter _, [1 to 5] filter-nums even #=> [2,4] filter-nums odd #=> [1,3,5] filter-nums (< 3) #=> [1,2]
var filterNums, slice$ = [].slice; filterNums = partialize$.apply(this, [filter, [void 8, [1, 2, 3, 4, 5]], [0]]); filterNums(even); filterNums(odd); filterNums((function(it){ return it < 3; })); function partialize$(f, args, where){ var context = this; return function(){ var params = slice$.call(arguments), i, len = params.length, wlen = where.length, ta = args ? args.concat() : [], tw = where ? where.concat() : []; for(i = 0; i < len; ++i) { ta[tw[0]] = params[i]; tw.shift(); } return len < wlen && len ? partialize$.apply(context, [f, ta, tw]) : f.apply(context, ta); }; }
If you call a partially applied function with no arguments, it will execute as is instead of returning itself, allowing you to use default arguments.
Partially applied functions are also really useful for piping if the functions you are using don't have a nice argument order and aren't curried (like in underscore.js for instance).
[1 2 3] |> _.map _, (* 2) |> _.reduce _, (+), 0 #=> 12
_.reduce(_.map([1, 2, 3], (function(it){ return it * 2; })), curry$(function(x$, y$){ return x$ + y$; }), 0); function curry$(f, bound){ var context, _curry = function(args) { return f.length > 1 ? function(){ var params = args ? args.concat() : []; context = bound ? context || this : this; return params.push.apply(params, arguments) < f.length && arguments.length ? _curry.call(context, params) : f.apply(context, params); } : f; }; return _curry(); }
Arguments
If you have only one argument, you can use it
to
access it without having to define an argument.
f = -> it + 2 f 3 #=> 5
var f; f = function(it){ return it + 2; }; f(3);
You can access the arguments
object with the
shorthand &
. The first argument is
&0
, the second &1
, and so on.
&
alone is arguments
as a whole.
add-three-numbers = -> &0 + &1 + &2 add-three-numbers 1 2 3 #=> 6
var addThreeNumbers; addThreeNumbers = function(){ return arguments[0] + arguments[1] + arguments[2]; }; addThreeNumbers(1, 2, 3);
Note that currying won't work in that situation, as the number
of declared arguments in add-three-numbers
is 0.
More
Check out the section on composing functions, and piping as well.
Generators and Yield
You can use generators and yield in your LiveScript code! A brief rundown:
function* f yield "foo" g = ->* yield from f! yield "bar" h = g! h.next!.value + h.next!.value #=> "foobar"
var g, h; function* f(){ yield "foo"; } g = function*(){ yield* f(); yield "bar"; }; h = g(); h.next().value + h.next().value;
You can create generators by either appending a star
*
to the function
keyword, or appending
it to LiveScript's arrow notation. This works with the variety of
arrows we have.
yield
is simply the same as in JavaScript, and
yield from
is JavaScript's yield*
.
To run code using generators and yield using node 0.11, use the
--harmony
flag. If running directly with
lsc
, use lsc file.ls --nodejs --harmony
to pass the harmony flag to node.
If and Unless
There are several ways to format an if
statement.
(Note that the if
statement is actually an expression,
and be used as such).
The standard:
if 2 + 2 == 4 'something' else 'something else' if 2 + 2 == 4 then 'something' else 'something else' if 2 + 2 == 4 then 'something' else 'something else'
if (2 + 2 === 4) { 'something'; } else { 'something else'; } if (2 + 2 === 4) { 'something'; } else { 'something else'; } if (2 + 2 === 4) { 'something'; } else { 'something else'; }
The else
is optional, and further else
if
s can be added.
if 2 + 2 == 4 'something' if 2 + 2 == 6 'something' else if 2 + 2 == 5 'something else' else 'the default'
if (2 + 2 === 4) { 'something'; } if (2 + 2 === 6) { 'something'; } else if (2 + 2 === 5) { 'something else'; } else { 'the default'; }
It can be used as an expression:
result = if 2 / 2 is 0 then 'something' else 'something else'
var result; result = 2 / 2 === 0 ? 'something' : 'something else';
It can also be used postfix - it has a lower precedence than assignment, making this useful:
x = 10 x = 3 if 2 + 2 == 4 x #=> 3
var x; x = 10; if (2 + 2 === 4) { x = 3; } x;
unless
is the equivalent to if
not
.
unless 2 + 2 == 5 'something' x = 10 x = 3 unless 2 + 2 == 5
var x; if (2 + 2 !== 5) { 'something'; } x = 10; if (2 + 2 !== 5) { x = 3; }
that
refers implicitly to the value of the
condition. It will unwrap existence checks.
time = days: 365 half-year = that / 2 if time.days #=> 182.5 if /^e(.*)/ == 'enter' that.1 #=> 'nter' if half-year? that * 2 #=> 365
var time, that, halfYear; time = { days: 365 }; if (that = time.days) { halfYear = that / 2; } if (that = /^e(.*)/.exec('enter')) { that[1]; } if ((that = halfYear) != null) { that * 2; }
Loops and Comprehensions
There are three basic forms of for
loop. One that
iterates through a range of numbers, one that iterates through
items in a list, and one that iterates through keys and values of
an object.
We will first examine the for
loop that iterates
through a range of numbers. It has the structure of: for
(let) (VAR) (from NUM) (to|til NUM) (by NUM) (when COND)
-
(almost everything is optional).
let
has the effect of wrapping the loop body in an
immediately invoked function expression, useful for when you are
creating functions in a loop and want the loop variables to be the
current - not final - values in the function when it is called.
It's also useful as it means the variables in your loop are not
exposed to the loops surrounding scope.
Counting from
a number, if omitted defaults to
0
.
Counting up to
and including a number, or counting
up til
(but not including) a number.
by
is the step value, the default is
1
.
when
, (alias case
, |
) is
an optional guard.
If used as an expression, loops will evaluate to a list.
for i from 1 to 10 by 3 i
var i$, i, ref$, len$, val, key; for (i$ = 1; i$ <= 10; i$ += 3) { i = i$; i; }
for ... in
loops iterate through a list. They have
the structure: for (let) (VAL-VAR)(, INDEX-VAR) in EXP (by
NUM) (when COND)
- again almost everything is optional.
let
, by
, and when
are as
before.
VAL-VAR
evaluates to the current value, while
INDEX-VAR
evaluates to the current index of the list.
Either one is optional.
EXP
should evaluate to an array.
for x in [1 2 3] x xs = for let x, i in [1 to 10] by 2 when x % 3 == 0 -> i + x xs[0]! #=> 5 xs[1]! #=> 17
var i$, ref$, len$, x, xs, res$; for (i$ = 0, len$ = (ref$ = [1, 2, 3]).length; i$ < len$; ++i$) { x = ref$[i$]; x; } res$ = []; for (i$ = 0, len$ = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].length; i$ < len$; i$ += 2) { if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10][i$] % 3 === 0) { res$.push((fn$.call(this, i$, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10][i$]))); } } xs = res$; xs[0](); xs[1](); function fn$(i, x){ return function(){ return i + x; }; }
for ... of
loops iterate through an object. They
have the structure: for (own) (let) (KEY-VAR)(, VAL-VAR) of
EXP (when COND)
- again almost everything is optional.
let
and when
are as before.
own
uses the hasOwnProperty
check on
the properties, stopping the iteration of properties higher up in
the prototype chain.
KEY-VAR
evaluates to the key of the property, and
VAL-VAR
evaluates to the property value. Either one is
optional.
EXP
should evaluate to an object.
for k, v of {a: 1, b: 2} "#k#v" xs = for own let key, value of {a: 1, b: 2, c: 3, d: 4} when value % 2 == 0 -> key + value xs[0]! #=> 'b2' xs[1]! #=> 'd4'
var k, ref$, v, xs, res$, i$, own$ = {}.hasOwnProperty; for (k in ref$ = { a: 1, b: 2 }) { v = ref$[k]; k + "" + v; } res$ = []; for (i$ in ref$ = { a: 1, b: 2, c: 3, d: 4 }) if (own$.call(ref$, i$)) { if (ref$[i$] % 2 === 0) { res$.push((fn$.call(this, i$, ref$[i$]))); } } xs = res$; xs[0](); xs[1](); function fn$(key, value){ return function(){ return key + value; }; }
Regular nested loops will evaluate to a list of lists.
result = for x to 3 for y to 2 x + y result #=> [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
var result, res$, i$, x, lresult$, j$, y; res$ = []; for (i$ = 0; i$ <= 3; ++i$) { x = i$; lresult$ = []; for (j$ = 0; j$ <= 2; ++j$) { y = j$; lresult$.push(x + y); } res$.push(lresult$); } result = res$; result;
You can omit one or both variables in
in
/of
loops.
res = for , i in [1 2 3] i res #=> [0, 1, 2] for til 3 then func! # calls func three times [6 for til 3] #=> [6, 6, 6]
var res, res$, i$, len$, i; res$ = []; for (i$ = 0, len$ = [1, 2, 3].length; i$ < len$; ++i$) { i = i$; res$.push(i); } res = res$; res; for (i$ = 0; i$ < 3; ++i$) { func(); } for (i$ = 0; i$ < 3; ++i$) { 6; }
List comprehensions always produce a list. Nested comprehensions produce a flattened list.
[x + 1 for x to 10 by 2 when x isnt 4] #=> [1,3,7,9,11] ["#x#y" for x in [\a \b] for y in [1 2]] #=> ['a1','a2','b1','b2']
var i$, x, ref$, len$, j$, ref1$, len1$, y; for (i$ = 0; i$ <= 10; i$ += 2) { x = i$; if (x !== 4) { x + 1; } } for (i$ = 0, len$ = (ref$ = ['a', 'b']).length; i$ < len$; ++i$) { x = ref$[i$]; for (j$ = 0, len1$ = (ref1$ = [1, 2]).length; j$ < len1$; ++j$) { y = ref1$[j$]; x + "" + y; } }
You can use whitespace to better format comprehensions.
[{id:id1, name, age} for {id:id1, name} in table1 for {id:id2, age} in table2 when id1 is id2]
var i$, ref$, len$, ref1$, id1, name, j$, len1$, ref2$, id2, age; for (i$ = 0, len$ = (ref$ = table1).length; i$ < len$; ++i$) { ref1$ = ref$[i$], id1 = ref1$.id, name = ref1$.name; for (j$ = 0, len1$ = (ref1$ = table2).length; j$ < len1$; ++j$) { ref2$ = ref1$[j$], id2 = ref2$.id, age = ref2$.age; if (id1 === id2) { ({ id: id1, name: name, age: age }); } } }
You can use the cascade to implicit refer to the value being mapped.
[.. + 1 for [1 2 3]] #=> [2, 3, 4] list-of-obj = * name: 'Alice' age: 23 * name: 'Betty' age: 26 [..name for list-of-obj] #=> ['Alice', 'Betty']
var i$, x$, ref$, len$, listOfObj, y$; for (i$ = 0, len$ = (ref$ = [1, 2, 3]).length; i$ < len$; ++i$) { x$ = ref$[i$]; x$ + 1; } listOfObj = [ { name: 'Alice', age: 23 }, { name: 'Betty', age: 26 } ]; for (i$ = 0, len$ = listOfObj.length; i$ < len$; ++i$) { y$ = listOfObj[i$]; y$.name; }
Object comprehensions produce an object.
{[key, val * 2] for key, val of {a: 1, b: 2}} #=> {a: 2, b: 4}
var key, ref$, val; for (key in ref$ = { a: 1, b: 2 }) { val = ref$[key]; [key, val * 2]; }
while
loops:
i = 0 list = [1 to 10] while n < 9 n = list[++i]
var i, list, n; i = 0; list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; while (n < 9) { n = list[++i]; }
until
is equivalent to while not
.
while
/until
can also accept a
when
guard, and an optional else
clause
which runs if the didn't run at all.
i = 1 list = [1 to 10] until i > 7 when n isnt 99 n = list[++i] else 10
var i, list, yet$, n; i = 1; list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; for (yet$ = true; !(i > 7);) { yet$ = false; if (n !== 99) { n = list[++i]; } } if (yet$) { 10; }
Do while:
i = 0 list = [1 to 10] do i++ while list[i] < 9
var i, list; i = 0; list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; do { i++; } while (list[i] < 9);
While can also accept an update clause.
i = 0 list = [1 to 10] while list[i] < 9, i++ then i
var i, list; i = 0; list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; for (; list[i] < 9; i++) { i; }
While true:
i = 0 loop \ha break if ++i > 20 i = 0 for ever \ha if ++i > 20 break
var i; i = 0; for (;;) { 'ha'; if (++i > 20) { break; } } i = 0; for (;;) { 'ha'; if (++i > 20) { break; } }
Switch
break
is automatically inserted, and multiple
conditions are allowed.
switch 6 case 1 then \hello case 2, 4 then \boom case 6 'here it is' default \something
switch (6) { case 1: 'hello'; break; case 2: case 4: 'boom'; break; case 6: 'here it is'; break; default: 'something'; }
If you switch on nothing, you switch on true. (It compile to
switching on false so it can cast the cases to booleans with one
!
instead of two).
switch case 5 == 6 \never case false 'also never' case 6 / 2 is 3 'here'
switch (false) { case 5 !== 6: 'never'; break; case !false: 'also never'; break; case 6 / 2 !== 3: 'here'; }
You can use fallthrough
to stop automatic
break
insertion. It must be the last expression of the
case block. As well, you can use a switch statement as an
expression.
result = switch 6 case 6 something = 5 fallthrough case 4 'this is it' result #=> 'this is it'
var something, result; result = (function(){ switch (6) { case 6: something = 5; // fallthrough case 4: return 'this is it'; } }()); result;
|
is an alias for case
, and
=>
is an alias for then
. |
otherwise
and | _
are aliases for
default
.
switch 'moto' | "some thing" => \hello | \explosion \bomb => \boom | <[ the moto ? ]> => 'here it is' | otherwise => \something
switch ('moto') { case "some thing": 'hello'; break; case 'explosion': case 'bomb': 'boom'; break; case 'the': case 'moto': case '?': 'here it is'; break; default: 'something'; }
You can use that
with a switch statement:
switch num | 2 => console.log that | otherwise => console.log that
var that; switch (that = num) { case 2: console.log(that); break; default: console.log(that); }
An implicit switch
is added after arrows (eg.
->
), :
, and =
if the next
token is case
.
func = (param) -> | param.length < 5 => param.length | otherwise => param.slice 3 func 'hello' #=> lo state = | 2 + 2 is 5 => 'I love Big Brother' | _ => 'I love Julia'
var func, state; func = function(param){ switch (false) { case !(param.length < 5): return param.length; default: return param.slice(3); } }; func('hello'); state = (function(){ switch (false) { case 2 + 2 !== 5: return 'I love Big Brother'; default: return 'I love Julia'; } }());
You can also use CoffeeScript style switch
statements.
day = \Sun switch day when "Mon" then 'go to work' when "Tue" then 'go to a movie' when "Thu" then 'go drinking' when "Fri", "Sat" 'go dancing' when "Sun" then 'drink more' else 'go to work'
var day; day = 'Sun'; switch (day) { case "Mon": 'go to work'; break; case "Tue": 'go to a movie'; break; case "Thu": 'go drinking'; break; case "Fri": case "Sat": 'go dancing'; break; case "Sun": 'drink more'; break; default: 'go to work'; }
Assignment
Basic assignment is as you would expect, variable =
value
, and there is no need for variable declarations.
However, unlike CoffeeScript, you must use :=
to
modify variables in upper scopes.
x = 10 do -> x = 5 x #=> 10 do -> x := 2 x #=> 2
var x; x = 10; (function(){ var x; return x = 5; })(); x; (function(){ return x = 2; })(); x;
Almost everything is an expression, which means you can do things like:
x = if 2 + 2 == 4 then 10 else 0 x #=> 10
var x; x = 2 + 2 === 4 ? 10 : 0; x;
Things such as loops, switch statements, and even try/catch statements are all expressions.
If you want to simply declare a variable and not initialize it,
you can use var
.
var x
var x;
You can also declare constants in LiveScript using
const
. They are checked at compile time - the compiled
JS is no different.
Attempting to compile the following:
const x = 10 x = 0
Results in redeclaration of constant "x" on line
2
.
However, objects are not frozen if declared as constants - you
can still modify their properties. You can force all variables to
be constants if you compile with the -k
or
--const
flags.
Operators
Compound assignment:
(?
, ||
, or &&
can
prefix any compound assign.)
x = 2 #=> 2 x += 2 #=> 4 x -= 1 #=> 3 x *= 3 #=> 9 x /= 3 #=> 3 x %= 3 #=> 0 x %%= 3 #=> 0 x <?= -1 #=> -1 x >?= 2 #=> 2 x **= 2 #=> 4 x ^= 2 #=> 16 x ?= 10 x #=> 16 x ||= 5 #=> 16 x &&= 5 #=> 5 x &&+= 3 #=> 8 x ?*= 2 x #=> 8 xs = [1 2] xs ++= [3] xs #=> [1 2 3]
var x, ref$, xs; x = 2; x += 2; x -= 1; x *= 3; x /= 3; x %= 3; x = ((x) % (ref$ = 3) + ref$) % ref$; x <= (ref$ = -1) || (x = ref$); x >= 2 || (x = 2); x = Math.pow(x, 2); x = Math.pow(x, 2); x == null && (x = 10); x; x || (x = 5); x && (x = 5); x && (x += 3); x == null && (x *= 2); x; xs = [1, 2]; xs = xs.concat([3]); xs;
Unary Assignment:
y = \45 + = y #=> 45 (make into number) !! = y #=> true (make into boolean) -~-~ = y #=> 3 (intcasting bicrement)
var y; y = '45'; y = +y; y = !!y; y = -~-~y;
Assignment defaults - you can use ||
,
&&
, and ?
.
You can use =
instead of ?
in function
parameters.
x ? y = 10 y #=> 10 f = (z = 7) -> z f 9 #=> 9 f! #=> 7
var y, f; (typeof x == 'undefined' || x === null) && (y = 10); y; f = function(z){ z == null && (z = 7); return z; }; f(9); f();
Soak assign - performs assign only if the right operand exists:
age = 21 x? = age x #=> 21 x? = years x #=> 21
var age, x; age = 21; if (age != null) { x = age; } x; if (typeof years != 'undefined' && years !== null) { x = years; } x;
Destructuring
Destructuring is a powerful way to extract values from variables. Rather than assigning to a simple variable, you can assign to data structures, which extract the values. For example:
[first, second] = [1, 2] first #=> 1 second #=> 2
var ref$, first, second; ref$ = [1, 2], first = ref$[0], second = ref$[1]; first; second;
You can also use splats:
[head, ...tail] = [1 to 5] head #=> 1 tail #=> [2,3,4,5] [first, ...middle, last] = [1 to 5] first #=> 1 middle #=> [2,3,4] last #=> 5
var ref$, head, tail, first, i$, middle, last, slice$ = [].slice; ref$ = [1, 2, 3, 4, 5], head = ref$[0], tail = slice$.call(ref$, 1); head; tail; ref$ = [1, 2, 3, 4, 5], first = ref$[0], middle = 1 < (i$ = ref$.length - 1) ? slice$.call(ref$, 1, i$) : (i$ = 1, []), last = ref$[i$]; first; middle; last;
...and objects too!
{name, age} = {weight: 110, name: 'emma', age: 20} name #=> 'emma' age #=> 20
var ref$, name, age; ref$ = { weight: 110, name: 'emma', age: 20 }, name = ref$.name, age = ref$.age; name; age;
You can also name the entity which you are destructuring using
:label
, as well as arbitrarily nest the
destructuring.
[[x, ...xs]:list1, [y, ...ys]:list2] = [[1,2,3],[4,5,6]] x #=> 1 xs #=> [2,3] list1 #=> [1,2,3] y #=> 4 ys #=> [5,6] list2 #=> [4,5,6]
var ref$, list1, x, xs, list2, y, ys, slice$ = [].slice; ref$ = [[1, 2, 3], [4, 5, 6]], list1 = ref$[0], x = list1[0], xs = slice$.call(list1, 1), list2 = ref$[1], y = list2[0], ys = slice$.call(list2, 1); x; xs; list1; y; ys; list2;
Substructuring
Easily set properties of lists and objects.
mitch = age: 21 height: 180cm pets: [\dog, \goldfish] phile = {} phile{height, pets} = mitch phile.height #=> 180 phile.pets #=> ['dog', 'goldfish']
var mitch, phile; mitch = { age: 21, height: 180, pets: ['dog', 'goldfish'] }; phile = {}; phile.height = mitch.height, phile.pets = mitch.pets; phile.height; phile.pets;
Property Access
The standard:
[1 2 3][1] #=> 2 {a: 1, b: 2}.b #=> 2
[1, 2, 3][1]; ({ a: 1, b: 2 }).b;
Dot Access - dot operators can accept many more things other than identifiers as their right operand, including numbers, strings, parentheses, brackets, and braces.
x = "hello world": [4 [5 boom: 6]] x.'hello world'.1.[0] #=> 5
var x; x = { "hello world": [ 4, [ 5, { boom: 6 } ] ] }; x['hello world'][1][0];
Accessignment using .=
.
document.title .= to-upper-case! #=> LIVESCRIPT ...
document.title = document.title.toUpperCase();
Array slice and splice:
list = [1 2 3 4 5] list[2, 4] #=> [3,5] list[1 to 3] #=> [2,3,4] list[1 til 3] #=> [2,3] list[1 til 3] = [7 8] list #=> [1,7,8,4,5]
var list, ref$; list = [1, 2, 3, 4, 5]; [list[2], list[4]]; [list[1], list[2], list[3]]; [list[1], list[2]]; ref$ = [7, 8], list[1] = ref$[0], list[2] = ref$[1]; list;
Object slice:
obj = one: 1, two: 2 obj{first: one, two} #=> {first: 1, two: 2}
var obj; obj = { one: 1, two: 2 }; ({ first: obj.one, two: obj.two });
Length star *
.
list = [1 2 3 4 5] list[*] = 6 list #=> [1,2,3,4,5,6] list[*-1] #=> 6
var list; list = [1, 2, 3, 4, 5]; list[list.length] = 6; list; list[list.length - 1];
Semiautovivification .{}
, .[]
ensures
that the property exists as an object or as an array.
x = "hello world": [4 [5 boom: 6]] x.[]'hello world'.1.{}1.boom #=> 6 x.[]arr.{}1.y = 9 x.arr.1.y #=> 9
var x, ref$; x = { "hello world": [ 4, [ 5, { boom: 6 } ] ] }; ((ref$ = (x['hello world'] || (x['hello world'] = []))[1])[1] || (ref$[1] = {})).boom; ((ref$ = x.arr || (x.arr = []))[1] || (ref$[1] = {})).y = 9;
Binding access .~
retrieves an object's method as
bound to the object. With automatic dot insertion you can just use
~
.
obj = x: 5 add: (y) -> @x + y target = x: 600 not-bound: obj.add bound: obj~add target.not-bound 5 #=> 605 target.bound 5 #=> 10
var obj, target; obj = { x: 5, add: function(y){ return this.x + y; } }; target = { x: 600, notBound: obj.add, bound: bind$(obj, 'add') }; target.bound(5); function bind$(obj, key, target){ return function(){ return (target || obj)[key].apply(obj, arguments) }; }
Cascades
A cascade always evaluates to the item being accessed, and not to the return value of the accessing operations.
Beautiful chaining:
a = [2 7 1 8] ..push 3 ..shift! ..sort! a #=> [1,3,7,8] document.query-selector \h1 ..style ..color = \red ..font-size = \large ..inner-HTML = 'LIVESCRIPT!'
var x$, a, y$; x$ = a = [2, 7, 1, 8]; x$.push(3); x$.shift(); x$.sort(); a; x$ = document.querySelector('h1'); y$ = x$.style; y$.color = 'red'; y$.fontSize = 'large'; x$.innerHTML = 'LIVESCRIPT!';
Cascades are callable, and can include arbitrary code.
console.log x = 1 y = 2 .. x, y # prints `1 2` to the console
var x$, x, y; x$ = console.log; x = 1; y = 2; x$(x, y);
You can use with
to specify the part of the
preceding expression to cascade.
x = with {a: 1, b: 2} ..a = 7 ..b += 9 x #=> {a: 7, b: 11}
var x, x$; x = (x$ = { a: 1, b: 2 }, x$.a = 7, x$.b += 9, x$); x;
Exceptions
You can throw exceptions with the aptly named
throw
.
throw new Error 'an error has occurred!'
throw new Error('an error has occurred!');
You can catch and deal with exceptions with the try
catch
finally
block. Both
catch
and finally
are optional.
The try
block is attempted. If an exception occurs,
the catch
block is executed. It is supplied with the
exception object. If you do not specify the name of this variable
it will default to e
. Unlike JavaScript, the exception
variable is scoped to the nearest function, not the
catch
block. You can destructure the exception object
if you wish.
try ... try ... catch 2 + 2 e.message x = try ... catch {message} message x #=> unimplemented
var e, x, message; try { throw Error('unimplemented'); } catch (e$) {} try { throw Error('unimplemented'); } catch (e$) { e = e$; 2 + 2; } e.message; x = (function(){ try { throw Error('unimplemented'); } catch (e$) { message = e$.message; return message; } }()); x;
The finally
block is executed after the
try
or catch
, regardless of what has
happened.
try ... catch handle-exception e finally do-something! try ... finally do-something!
var e; try { throw Error('unimplemented'); } catch (e$) { e = e$; handleException(e); } finally { doSomething(); } try { throw Error('unimplemented'); } finally { doSomething(); }
Object Oriented Programming
Classes are simple sugar for the definition of constructor functions and the setting of their prototype.
The constructor function is defined as function literal at the top level of the class definition.
Properties of its prototype are defined by object literals at the top level.
class A (num) -> @x = num property: 1 method: (y) -> @x + @property + y a = new A 3 a.x #=> 3 a.property #=> 1 a.method 6 #=> 10
var A, a; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; function A(num){ this.x = num; } prototype.property = 1; prototype.method = function(y){ return this.x + this.property + y; }; return A; }()); a = new A(3); a.x; a.property; a.method(6);
Static properties (properties attached to the constructor) are
defined by adding properties to this
at the top level.
These properties can be accessed in methods by accessing
constructor
(shorthand @@
).
class A @static-prop = 10 get-static: -> @@static-prop + 2 A.static-prop #=> 10 a = new A a.get-static! #=> 12
var A, a; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; A.staticProp = 10; prototype.getStatic = function(){ return constructor.staticProp + 2; }; function A(){} return A; }()); A.staticProp; a = new A; a.getStatic();
Private static properties are defined as just regular variables in the class body. (note: private instance properties are not possible in JavaScript, and thus, LiveScript.)
class A secret = 10 get-secret: -> secret a = new A a.get-secret! #=> 10
var A, a; A = (function(){ A.displayName = 'A'; var secret, prototype = A.prototype, constructor = A; secret = 10; prototype.getSecret = function(){ return secret; }; function A(){} return A; }()); a = new A; a.getSecret();
You can define bound methods (using ~>
, which
have their definition of this
bound to the
instance.
class A x: 10 bound-func: (x) ~> @x reg-func: (x) -> @x a = new A obj = x: 1 bound: a.bound-func reg: a.reg-func obj.bound! #=> 10 obj.reg! #=> 1
var A, a, obj; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; prototype.x = 10; prototype.boundFunc = function(x){ return this.x; }; prototype.regFunc = function(x){ return this.x; }; function A(){ this.boundFunc = bind$(this, 'boundFunc', prototype); } return A; }()); a = new A; obj = { x: 1, bound: a.boundFunc, reg: a.regFunc }; obj.bound(); obj.reg(); function bind$(obj, key, target){ return function(){ return (target || obj)[key].apply(obj, arguments) }; }
You can easily set properties in constructor functions and in methods using the object setting parameter shorthand.
class A (@x) -> f: (@y) -> @x + @y a = new A 2 a.x #=> 2 a.f 3 #=> 5 a.y #=> 3
var A, a; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; function A(x){ this.x = x; } prototype.f = function(y){ this.y = y; return this.x + this.y; }; return A; }()); a = new A(2); a.x; a.f(3); a.y;
If you define the constructor as a bound function
~>
, you don't need to use new
when
making a new instance.
class A (@x) ~> a = A 4 a.x #=> 4
var A, a; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; function A(x){ var this$ = this instanceof ctor$ ? this : new ctor$; this$.x = x; return this$; } function ctor$(){} ctor$.prototype = prototype; return A; }()); a = A(4); a.x;
For higher level libraries and frameworks, there is the ability
to define the constructor as an external function, by setting the
property constructor$$
. This is not recommended for
regular coding.
f = (@x) -> class A constructor$$: f a = new A 5 a.x #=> 5
var f, A, a; f = function(x){ this.x = x; }; A = (function(){ A.displayName = 'A'; var constructor$$, prototype = A.prototype, constructor = A; function A(){ return constructor$$.apply(this, arguments); } constructor$$ = f; return A; }()); a = new A(5); a.x;
You can inherit with extends
class A -> @x = 1 @static-prop = 8 method: -> @x + 2 class B extends A -> @x = 10 B.static-prop #=> 8 b = new B b.x #=> 10 b.method! #=> 12
var A, B, b; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; function A(){ this.x = 1; } A.staticProp = 8; prototype.method = function(){ return this.x + 2; }; return A; }()); B = (function(superclass){ var prototype = extend$((import$(B, superclass).displayName = 'B', B), superclass).prototype, constructor = B; function B(){ this.x = 10; } return B; }(A)); B.staticProp; b = new B; b.x; b.method(); function extend$(sub, sup){ function fun(){} fun.prototype = (sub.superclass = sup).prototype; (sub.prototype = new fun).constructor = sub; if (typeof sup.extended == 'function') sup.extended(sub); return sub; } function import$(obj, src){ var own = {}.hasOwnProperty; for (var key in src) if (own.call(src, key)) obj[key] = src[key]; return obj; }
This is especially useful with super
. If bare,
super
is a reference to the appropriate function. If
you want to call it with all arguments, use super
...
.
class A -> @x = 1 method: (num) -> @x + num class B extends A -> @y = 2 super! method: (num) -> @y + super ... b = new B b.y #=> 2 b.method 10 #=> 13
var A, B, b; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; function A(){ this.x = 1; } prototype.method = function(num){ return this.x + num; }; return A; }()); B = (function(superclass){ var prototype = extend$((import$(B, superclass).displayName = 'B', B), superclass).prototype, constructor = B; function B(){ this.y = 2; B.superclass.call(this); } prototype.method = function(num){ return this.y + superclass.prototype.method.apply(this, arguments); }; return B; }(A)); b = new B; b.y; b.method(10); function extend$(sub, sup){ function fun(){} fun.prototype = (sub.superclass = sup).prototype; (sub.prototype = new fun).constructor = sub; if (typeof sup.extended == 'function') sup.extended(sub); return sub; } function import$(obj, src){ var own = {}.hasOwnProperty; for (var key in src) if (own.call(src, key)) obj[key] = src[key]; return obj; }
You can use mixins through implements
. You can only
inherit from one class, but can mixin as many objects as you like.
Remember, if you want to implement a class, not just a simple
object, you must implement the class's prototype.
Renameable = set-name: (@name) -> get-name: -> @name ? @id class A implements Renameable -> @id = Math.random! * 1000 a = new A a.get-name! #=> some random number a.set-name 'moo' a.get-name! #=> 'moo'
var Renameable, A, a; Renameable = { setName: function(name){ this.name = name; }, getName: function(){ var ref$; return (ref$ = this.name) != null ? ref$ : this.id; } }; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; importAll$(prototype, arguments[0]); function A(){ this.id = Math.random() * 1000; } return A; }(Renameable)); a = new A; a.getName(); a.setName('moo'); a.getName(); function importAll$(obj, src){ for (var key in src) obj[key] = src[key]; return obj; }
To modify the prototype, you can use the shorthand
::
, and you can use the ::=
operator if
you wish to modify several properties.
class A prop: 10 f: -> @prop a = new A b = new A a.f! #=> 10 A::prop = 6 a.f! #=> 6 b.f! #=> 6 A ::= prop: 5 f: -> @prop + 4 a.f! #=> 9 b.f! #=> 9
var A, a, b, ref$; A = (function(){ A.displayName = 'A'; var prototype = A.prototype, constructor = A; prototype.prop = 10; prototype.f = function(){ return this.prop; }; function A(){} return A; }()); a = new A; b = new A; a.f(); A.prototype.prop = 6; a.f(); b.f(); ref$ = A.prototype; ref$.prop = 5; ref$.f = function(){ return this.prop + 4; }; a.f(); b.f();
If you don't wish to support older browsers, and desire the use
of Object.defineProperty
, you can use the following
shorthand:
class Box dimensions:~ -> @d ([width, height]) -> @d = "#{width}x#height" b = new Box b.dimensions = [10 5] b.dimensions #=> '10x5'
var Box, b; Box = (function(){ Box.displayName = 'Box'; var prototype = Box.prototype, constructor = Box; Object.defineProperty(prototype, 'dimensions', { get: function(){ return this.d; }, set: function(arg$){ var width, height; width = arg$[0], height = arg$[1]; this.d = width + "x" + height; }, configurable: true, enumerable: true }); function Box(){} return Box; }()); b = new Box; b.dimensions = [10, 5]; b.dimensions;
CoffeeScript to LiveScript Conversion Guide
- Change all your fat arrows
=>
to wavy arrows~>
- Change all your block comments
### ###
to/* */
- Change all
undefined
tovoid
- Change any range syntax creating lists from
[x..y]
to[x to y]
and change[x...y]
to[x til y]
. If your range goes downwards, ie. from a larger number to a smaller number, and it is not obvious (the from and to are not literals), then you must addby -1
, eg.[x to -3 by -1]
- Similarly, change any range syntax in for loops from
for i in [x..y]
tofor i from x to y
and changefor i in [x...y]
tofor i from x til y
- Change any list comprehensions from
(x for x in list)
to[x for x in list]
. Any postfix loops which you do not want returning a list, change to not postfixed, eg. changeincrease x for x in list
tofor x in list then increase x
. You can shave characters if you wish by using the alias tothen
, the fat arrow=>
- Change any number literals starting with a dot, eg.
.5
to start with a zero0.5
- Change any regular expression literals from
/// ///
to// //
- Change any splats you are using from
(args...) ->
to be prefixed like(...args) ->
- Remove the parentheses from function definitions with no
parameters,
() ->
simply becomes->
- this is because()
is always a call. - Change your constructor functions in your classes from being
defined as
class Item constructor: ->
to simply being functions at the top level of your class body, eg.class Item ->
If your constructor is an external function, define it with the special propertyconstructor$$
. - Change any calls to super from
super
tosuper ...
- this is becausesuper
is a direct reference to the parent function rather than a call itself. - Change any bitwise operators to themselves surrounded by dots.
Eg.
&
is now.&.
and>>
is now.>>.
- If you are modifying variables in upper scopes at any time, eg.
x = 10 do -> x = 5
you must use:=
instead of just=
as that will declare a new (shadowing) variable in LiveScript. Thus, the above code would need to bex = 10 do -> x := 5
if you wanted to modifyx
- If you are using nested list comprehensions (eg.
(x + y for x in [1, 2] for y in [3, 4])
, you will need to change this to a regular (non-postfixed) nested loop, as instead of the result being a lists within a list as in CoffeScript (the result of the example would be[[4,5],[5,6]]
), the result is flattened (the result of the example in LiveScript would be[4,5,5,6]
). Eg. changeresult = (x + y for x in [1, 2] for y in [3, 4])
toresult = for x in [1, 2] for y in [3, 4] x + y
in order to get the non-flattened result. - Change the name of any of your variables named
it
,that
,fallthrough
, orotherwise
. Those are terrible names for variables so you should change them anyway. This technically isn't required in all cases, but will be less confusing if you do so in all cases.it
is used as the implicit argument of functions defined with no parameters, eg.reverse = -> it.reverse!
.that
refers to the value of the condition, for instancethat + 2 if (x + 10)/(y - 18)
,that == (x + 10)/(y-18)
.fallthrough
, if used at the end of a case block makes that block fallthrough to the next case.otherwise
, if used directly after case, turns that case into default. - If you have any multiline strings non triple quoted strings
(using
"string"
or'string'
), eg.text = "hi there"
you will have to change them as in CoffeeScript this would be"hi there"
while LiveScript ignores indentation after newlines so it would be"hi there"
and
,or
and spaced.
and?.
close implicit calls, so you will have to change any code involving those where you have depended on CoffeeScript to not close the call. Eg.f a .g b or h c
would bef(a.g(b || h(c)))
in CoffeeScript andf(a).g(b) || h(c)
in LiveScript. You can use||
instead ofor
and&&
instead ofand
as those versions do not close implicit calls.- Change any
do
s special casing function literals to usinglet
. Eg. in CoffeeScriptdo ($ = jQuery) -> $
change tolet $ = jQuery $
- Change any implicit calls against blocks starting with an
implicit objects to use
do
. Eg. changef a: b
intof do a: b
- Change any JavaScript code literals, eg.
`js code here`
to``js code here``
- Change any dually spaced property access, eg.
x . y
to be either spaced on only one side or not at all. A dually spaced dot is now used to compose functions. - Change any non spaced identifier subtraction, eg.
a-b
to spaceda - b
.a-1
and1-b
are still fine. This is becausea-b
is a valid identifier in LiveScript, equivalent to its camel case,aB
.
Changelog
1.3.1
- Fixed bug with calling lsc file.ls with arguments (#569)
- Some object comprehension fixes
- Fixed prededence of
return
andthrow
in respect to pipes. - Added back the
--require
option missing in 1.3.0, this option is great when starting the REPL. Also made modules accesible in a similar manner torequire!
. Eg.--require './boom.js'
requires the file and allows you to access it withboom
. - Fixed bug with
require!
compilation - an error is now thrown when it should be
1.3.0
- Added support for generators and yield
- Improved
require!
, more like regular destructering now - Fixed pipe and assign precedence
- Removed auto infused IIFE, just use
for let
now - Browser versions changed to use browserify
- Option parsing uses Optionator
- Added
--no-header
option, to remove "Generated by..." header - Removed
livescript
from bin, just uselsc
- Removed Slake and Slakefiles
- Can now use
that
in the default case of a switch statement
1.2.0
- Bug fixes
- Better compilation of partially applied functions, eg.
10 |> f _, 2
is the same asf 10, 2
when compiled - Better compilation of compose - no longer using helper function
- Using prelude.ls version 1.0.3
--prelude/-d
flag only used for repl now, does not add prelude.ls include when compiling- Generated header is added to the top of compiled files, specifying which version of LiveScript the JS was compiled with
- Removed single line cascade due to lack of usefulness and ambiguity with other syntax
- Removed deprecated features - inexistance op
!?
,+++
,where
,undefined
(alias to void), short function syntax - To hush functions (stop them from automatically adding a return
statement), you can now always add a
!
in front of the arrow. Eg.(x) !-> x + x
- Autocompile
.json.ls
to.json
- Added
for let
1.1.1
- Fix/close #194, #206, #150, #217, #236, #238, #151, #215, #213, #237, #232, #161, #241
- Added
++
as array concat operator, to replace+++
(which is now deprecated). - Added
++=
operator to complement the above. - Added quick map in comprehensions, using cascade reference. For
example, instead of doing
[x + 1 for x in xs]
you can now write[.. + 1 for xs]
,..
being the implicit value. See section for more details. - Added the ability to have unary operators in parameters. Eg.
(!x) -> ...
desugars to(x) -> x = !x; ...
. You can use this to cast parameters to either booleans(!!x) ->
or numbers(+x) ->
. You can also clone objects so that you don't modify the original:(^^x) ->
. See section for more. - Bound curried functions
~~>
actually work now, and partially applied functions, eg.f _, 1
are bound at the first time they are partially applied. - Deprecated the
+++
array concatenation operator, please use++
instead.+++
will be removed in the next major LiveScript release. - Deprecated the
where
statement. Please uselet
and/or local variables instead.where
will be removed in the next major LiveScript release. - Deprecated
undefined
as an alias tovoid
. Please usevoid
instead.undefined
will be removed in the next major LiveScript release. - Deprecated the
!?
inexistence operator. When used postfix, please negate the existence operator instead, eg. changex!?
tonot x?
. If used as a logic operator, please use an if statement and the existence operator instead, eg. changex !? f!
tof! if x?
.!?
will be removed in the next major LiveScript release. - Please also remember that the short function syntax is also
currently deprecated and will be removed in the next major
LiveScript release. Eg.
f(x) = ...
type functions - please use the long arrow to defined curried functions instead, eg.f = (x) --> ...
.
1.1.0
lsc
is now an alias for thelivescript
command, aslivescript
is quite long. Think "LiveScript Compiler".- Added single line cascades. Eg.
[1 2 3]..push 3 ..shift!..sort!
- Cascades are callable, eg.
(+) .. 2 3
- Removed previous usage of
with
, uselet @ = x
instead. with
can be used to specify the part of the expression to cascade. Eg.x = with [1 2 3] ..push 3
- An empty catch
try x catch
returns its catchee. by -1
is not required in ranges where it is obvious.- Added shorthand for
constructor
:@@
- Changed semiautovivification from using
x@y
andx@@y
to usingx{}y
andx[]y
- Nested loops (not comprehensions) no longer return flattened
results when used as an expression.
res = for x to 2 for y to 3 "#x#y" res #=> [ ['00', '01', '02', '03'], ['10', '11', '12', '13'], ['20', '21', '22', '23'] ]
Use comprehensions instead if you want a flattened result. To make things easier, comprehensions can now be multiline.["#x#y" for x to 2 for y to 3] #=> ['00', '01', '02', '03', '10', '11', '12', '13', '20', '21', '22', '23']
- Fixed bug where comprehensions inside comprehensions were
returning flattened results, eg.
[[x + y for x to 2] for y to 3] #=> [ [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5] ]
now returns an array of arrays as expected. If you were relying on this behavior, simply remove the brackets of the inner comprehension to make it a nested comprehension. - Added
require!
, which takes either a object, array, or string literal and creates a series of require statements. See documentation. - Added the ability to call
super
outside of the class body, when adding static or instance methods to a class. Eg.class A extends B A::meth -> super ... A.stat -> super ...
- The 'from' number in a range can be omitted, to be consistent
with for loops. If the number is omitted, it is assumed to be
0
.[to 3] #=> [0, 1, 2, 3] [til 3] #=> [0, 1, 2]
- You can now define an external constructor in a class. This is
mostly for compatibility with CoffeeScript, and for higher level
libraries and frameworks. It is defined with
constructor$$:
. eg.class A constructor$$: some-external-func
- You can now splat
new
eg.new A ...a
- Class bodies are now executable. You can do things like:
class A if condition method: -> ... else method2: -> ... for key of obj (key): -> ...
- If you
do
a named function, and thedo
statement is not being used as an expression, the function retains its hoisting. Eg.f 1 #=> 1 do function f x x
compiles tof(1); function f(x){ return x; } f();
- You can now omit all variables from for loop definitions over a
range. Eg.
[1 for til 3] #=> [1, 1, 1] for til 3 then f! [f! for from 1 to 5]
- Added
delete!
statement, likedelete
in JavaScript. This is slightly different fromdelete
in LiveScript, which returns the deleted item if used as an expression.delete! x.y
- Added back JavaScript code literals, for the sole purpose of
allowing CoffeeScript to LiveScript compilation compatibility. Not
to be used in regular code. JS literals are denoted with two
backticks.
``js code``
for
loop and comprehension indices are immutable. Use awhile
loop if you want to mutate indices. Also, the last value of the index is the last value it has in the loop.- You can destructure the
catch
parameter. in
works on sparse arrays for compatibility with CoffeeScript.- Bug fixes.
1.0.1
- Bug fixes.
1.0.0
- Nicer compilation for the compose op
- The
--prelude
/-d
flags work for the repl - New func syntax eg.
f(x) = x
now uses~
for bound funcs, liked named funcs, eg.~f(x) = x
- Removed no return option for new func syntax
- Added function keyword arguments ala python, eg.
f = ({x = 2, y = 3}) -> x + y
, and then application withf y: 4
- Removed overloading of
+
for list concatination (just use+++
) - Readded bitwise compound assign, eg.
.&.=
- Added
where
, likelet
but comes after instead of before - also allows for nested assignments - Changed fuzzy equals (
==
in JS) to~=
,!~=
for the negation - Added
xor
operator, exclusive or - true if only one side or the other is true - Doing equals on a regex literal will compile to an exec, eg.
x == /[ae]/g
is/[ae]/g.exec(x)
- you can usethat
to get the results if used in an if statement. The negation simply uses test. - Binary logic is callable, eg.
(f or g) x
isf(x) || g(x)
- Allow splicing like in CoffeeScript, eg.
list[1 to x] = [1 2 3]
- Static functions are now inherited and support calls to
super
- Generated reference variables are now named with a
$
after them instead of__
preceding. eg.ref$
instead of__ref
- Removed constructor shorthand, just use
constructor
- Added cascade op from coco
..
- Added deep compare, allows the comparison of objects, arrays,
and more.
===
- experimental - Added
match
statement - experimental
0.9.12
- Changed bitwise/shift operator syntax to be more clear. Now
simply as in JS, but surrounded by dots. Eg.
&
is.&.
, and>>
is.>>.
- Speed improvements
- Allowed arbitrary expressions in slicing syntax, eg.
list[1 to x]
- Made classes work more like in CoffeeScript - details:
- Constructors are now inherited
- Allowed empty class definitions, eg.
class A extends B
- Bound methods (eg.
f: ~>
) are bound to the instance, not to the class
0.9.11
- Bug fixes
- Updated to work with node 0.8.x
- Added partial application of funcs using
_
placeholder - Added infix
with
"cloneport", clones head and imports tail into that - Allow partial application of pipe
|>
and backpipe<|
and use as function - Better compilation of various operators-as-functions
- Added existential soak implicit call/lookup, eg.
(?length)
- Allow partially applied assign -
=
,+=
, etc. - Removed
|>>
pipe operator - use normal pipes and partial application now - Changed backcall placeholder to underscore
_
- Added underscore
_
as alias tootherwise
in switch statements - Added
--prelude
or-d
option to automatically include prelude.ls in compiled files - Added
--const
or-k
option to compile as if all variables are constants - Added partial application of property accessor eg.
(obj.)
- Removed cons
&
operator - Added
&
as alias toarguments
, allowing-> &0 + &1
etc. - Removed
@@
as alias toarguments
- Increased precedence of the pipe
|>
operator to allow assignment of the entire thing without parens, not just of the first section - Added mixin feature
implements
thanks to Coco, eg.class Cow extends Animal implements Mooer
, which does::<<< Mooer
in the class body.
0.9.10
- Now have `const` and `var` thanks to Coco
- Implicit access and call functions, eg.
(.length)
and(.join \|)
- useful for mapping/filtering, etc. - Bug fixes
- Improved repl
0.9.9
- Various bug fixes
- Calling a curried funciton with no args calls it as is rather than returning itself
- Allow partial application of second arg for infix functions
0.9.8
- Object comprehensions added
- Dashes in identifiers allowed (transformed to camel case)
0.9.7
- Bug fixes
- Precedence change of
&
and+++
- Nicer pipe
|>
compilation
0.9.6
- Partially applied operators
- Operators as functions
0.9.5
- Infix function calls added
- JS literals removed
- Improved CLI
0.9.4
- Added proper list comprehensions
- Removed post-fix loops
0.9.3
- Changed node requirements
0.9.2
- Improved CLI
- Enabled expressions in range syntax
0.9.1
- Changed number radix symbol to
~
- Added
%%=
0.9.0
- Initial public release, many changes. See Changes from Coco for more details.
For more details see Changes from Coco.
Name
LiveScript was one of the original names for JavaScript, so it seemed fitting. It's an inside joke for those who know JavaScript well.
Thanks
You can find the full list of contributors here. That list includes contributors to LiveScript and its predecessors.
People who have contributed directly to LiveScript include George Zahariev, Satoshi Murakami, Joshua Weinstein, Josh Perez, Paul Miller, Nami-Doc, killdream, audreyt, clkao, viclib, dtinth, and racklin.
Thank you to Niels Groot Obbink for giving me the livescript.org domain name.
An extra special thanks to Satoshi, as this project is a fork of his project Coco and would not be possible without it. It has been a pleasure to work off of his beautiful Coco compiler.
Contributing Guide
Fork LiveScript
and make your changes. Always write tests first (in the
/test
directory).
Check out the Makefile included - it has all the available commands.
Always make sure you can make full
- ie. build the
compiler itself. Useful: git checkout -- lib && make
full
: cleans out your lib
and compiles twice
and tests. Only send a pull requests if all tests pass. Once all
tests pass, rewrite any of the compiler that needs rewriting, and
then make full
. Use make test-harmony
to
test with generators and such with node's --harmony
flag.
Don't submit patches with a built browser file
(browser/livescript.js
) created by make
build-browser
. This file is only built before releasing a
new version.
Changes from Coco: Detail and Rationale
This section is has not been updated since 1.0.x
- Renamed everything from Coco and Coke to LiveScript and Slake, and file extension from .co to .ls. Rationale for names chosen: LiveScript was the name of JavaScript before it was named JavaScript - thus it seemed like an appropriate name, also few if any other projects are named LiveScript. Slake because lake was taken and lsake sounds bad.
- Switched so that
==
compiles into===
, and also for the negatives. Rationale: Most people would want to use the JavaScript===
more often than==
and less typing is better, also this makes things more similar to CoffeeScript which compiles==
to===
so there is less code for people to change coming from CoffeeScript. The compilation ofis
to===
stays the same. - Switched
in
andof
so that they are like in CoffeeScript. In goes over values, of over keys. Rationale: People have to change less of their CoffeeScript code, they're used to it, and usingin
for checking if a value is in an array just seems right, usingof
just feels weird. - All bitwise operators except
~
have changed to be surrounded by dots, eg.&
is now.&.
and>>
is.>>.
. Bitwise assign equals (eg.&=
) have been removed. Rationale: People rarely use the bitwise operators all the time, and they take up valuable symbols that could be used for other purposes. They are still available, just in a longer form.~
is still there as is. =>
, the pipe operator using_
, is removed. Free up => (forthen
alias), (|
is used as an alias for case). Use other pipes (|>
) and partial application instead.|
is an alias forcase
(used in switch) Rationale: less typing, looks good. Modelled after Haskell's guards.=>
is an alias forthen
. Rationale: will not be encouraged for use in if statements as it looks slightly odd - really for use in switch statements, when combined with|
, to create a succinct and easy to understand structure. Based off of Haskell's use of -> in case expressions.- Added
otherwise
and underscore_
as a contextual keyword, when used aftercase
or|
, making that the default statement. Rationale: same as in Haskell (otherwise), underscore as in Scala - and shorter. It allows| otherwise => 4 + 9
, which fits in with the rest of the structure. - Added implicit
switch
after->
,~>
,:
, or an assign when they are followed by case tokens (eithercase
or|
). Rationale: reduces typing and increases beauty in a common situation for using a switch, with no increase in ambiguity. - Added list concat operator,
+++
. Eg.xs +++ ys
isxs.concat(ys)
. Rationale: less typing, more beautiful, inspired by the ++ function in Haskell (had to use 3 pluses in order to avoid ambiguity with increment operator.) ^
is now an alias to**
, the power operator. Rationale: it was available, and is used in other languages.- Power precedence is now proper, and the power operator has precedence over multiplication and division. It also has higher precedence than unary ops. Eg. 2*4^2 == 32, not 64 as in Coco. Also, -2^2 == -4. Rationale: math should work properly - this is how it's done in many languages including Haskell.
- Power operator is now right associative. eg. 2^2^3 == 2^(2^3) == 256. Rationale: follwing Haskell's and many other languages lead on this one.
- Added magic auto curried functions, defined using
-->
and~~>
. With this you can dotimes = (x, y) --> x * y
,timesTwo = times 2
,timesTwo 4 #=> 8
. If you call a curried function with no arguments, it calls itself as is, instead of returning itself (you can just reference it if you want it itself). Rationale: more Haskell like, useful functionality. - Added new function definition syntax, eg.
add(x, y) = x + y
==add = (x, y) --> x + y
. You can also use it in object literals and class definitions eg.add(x, y): x + y
==add: (x, y) --> x + y
. You can also suppress return on both by starting with a bang, eg.!nothingness(params) = true
will not return anything. As well you can have lexically bound this usingid@(param) = something
which is suger forid = (param) ~~> something
(notice the wavy arrow). You can go crazy and do something like this if you wish:@!func@! = something
which is a function assigned to this, which takes no parameters and returns nothing, while being lexically bound to this. All functions defined using this syntax are curried. Rationale: more beautiful, less typing, more Haskell like. - Added
obj ::= obj2
as alias toobj::<<<obj2
. Rationale: seems to be the intuitive behavior expected, looks cleaner. - Added
yes / on
as aliases totrue
andno / off
as aliases tofalse
. Addedundefined
as an alias tovoid
, and aliasedisnt
tois not
. Rationale: ease transition from CoffeeScript, which has all these features, to LiveScript. - Added
when
as alias tocase
(and|
). Rationale: ease transition from CoffeeScript. - Allowed guards in loops using case statement, eg.
x for x from 1 to 10 when x % 2 is 0
. Rationale: ease transition from CoffeeScript and have guards in comprehensions like other languages (Haskell). - Added the use of
else
for default in switch. Rationale: ease transition from CoffeeScript. - Added
loop
as alias forwhile true
. Rationale: ease transition from CoffeeScript. - Added pipe operator
|>
ala F#,val |> func
is the same asfunc(val)
. Very useful in combination with curried functions. Rationale: useful, as in F#. - Added backwards pipe operator
<|
ala F#:f <| x
is the same asf x
. This is more useful than it looks, and can help you avoid parens. For instance for a definition of toCase that returns a function that either uppercases or lowercases a word,toCase \up <| \hello
rather thantoCase(\up) \hello
. Rationale: useful as per above, as in F#. - Added function composition operators ala F#.
>>
and<<
.(f >> g) x
isg(f(x))
and(f << g) x
isf(g(x))
. Rationale: very useful, especially with curried functions. As in F# (and Haskell, there it's the dot operator). - Added
%%
operator (and corresponding%%=
operator),x %% y
is(x % y + y) % y
. Eg.-3 % 4 == -3; -3 %% 4 == 1
. Rationale: this is how the%
op behaves in other languages such as Python and Ruby. - Changed number base format from
7r4
to7~4
. This is because usingr
causes ambiguity with number comments. For instance,36rpm
- is that the number36
with the number commentrpm
or is itpm
base36
(922). Also now accept any number as radix, so that a better error can be thrown when that radix is not 2-36. Rationale: fixes ambiguity. - Allowed expression in range syntax, eg.
[x to y/2]
instead of just number literals. This has the side effect of removing naked ranges, eg.2 to 5
without enclosing brackets (for loops remain unchanged). Rationale: expressions in ranges are useful, much more than naked ranges which have really limited use. Also closer to CoffeeScript, easing transition again. - Changed CLI to bare compilation so that defined variables are
attached to global scope. Improved repl to continue when lines end
with
->
,~>
, or=
- Added list comprehensions (eg.
[x for x in list when x is \something]
) and removed postfix loops (eg.something x for x in list
). Comprehensions always return a list. Nested list comprehensions behave properly, ie. the last for is the inner most loop. Rationale: following Haskell and Python. More consistent behavior. Easier to deal with in your head. More intuitive. - Changed JS literals to use two backticks, eg.
``js code here``
instead of`js code here`
. Rationale: it was seldom needed; frees up backticks for infix function application. - Add infix function application, eg.
3 `add` 2
. Rationale: as in Haskell. This allows for partial application of their second argument, like operators eg.(`times` 2)
. - Changed unary clone operator from
^
to^^
. Rationale: disambiguate with partial application of power operator. - Added partial application of operators. Eg.
(+ 2)
returns a function which adds 2 to its argument. Also allowed is simply(*)
which is a function which multiplies its two arguments.(+x)
and(-x)
still mean the application of those unary operators - the operator must be spaced to be the partial application of that operator. Using the length star*
is no longer allowed within parens (sometimes used when setting dynamic keys), due to ambiguity with the partially applied multiplication operator. Rationale: awesome. As in Haskell. - Added unary operators as functions. Eg. you can use
(not)
as a function, compose it, etc. Rationale: very useful, as in Haskell. - Added
.
as alias to<<
in composing functions. This disallows property access in the style ofx . y
. Rationale: as in Haskell. Dually spaced property access should never be used anyway. - Added object comprehensions. Eg.
{[key, val * 2] for key, val of obj}
would return an object with all the values double of the original. The first part of the array literal gets translated into the key, the second part into the value. Rationale: very useful when dealing with objects. Can now map and filter objects back into objects. - Dashes are now supported in identifiers, they get converted
into camel case. Eg.
get-room
is equivalent togetRoom
,encode-URI
isencodeURI
. Rationale: enable different styles which other people may enjoy. As in the lisp family of languages. - Added implicit call/lookup eg.
(.length)
is equivalent to-> it.length
and(.join \*)
is equivalent to-> it.join \*
.(obj.)
is-> obj[it]
. Rationale: useful when mapping, filtering, etc. - Added infix
with
operator ("cloneport"). Eg.personA = personB with name: \alice
is equivalent topersonA = ^^personB <<< name: \alice
. Ie. it clones the head and imports the tail into that new object.personB
is unmodified. Rationale: really nice may to create new objects out of old ones. - Add partial application,
_
is the placeholder. Eg.f = add-three-numbers 1, _, 3
thenf 2 #=> 6
. Can be multiple times, and like curried functions if called with no args will invoke, allowing use of default args. Rationale: really useful, for when function args aren't in a nice order so you can use a curried version. - Changed backcall
<-
placeholder to underscore_
. Rationale: fit in with partial application placeholder. - Changed
arguments
alias to&
, removed@@
as that alias. Eg.-> &0 + &1
. Rationale: shorter - less typing. - Added
-d, --prelude
option for automatically adding prelude.ls. Rationale: make it easier to use prelude.ls. - Added
-k, --const
option for compiling as if all variables as constants. Rationale: some people coming from languages with all immutable values may find this nice. - Allowed arbitrary expressions in slicing syntax, eg.
list[1 to x]
. Rationale: useful. - Changed so that constructors are inherited. Rationale: useful, and more like in CoffeeScript.
- Allowed empty class definitions, ie. with no block. Rationale: useful and more like CoffeeScript.
- Bound methods are bound to the instance, not the class. Rationale: seems pretty useless to be bound to the class, so this is more useful. Also more like CoffeeScript.
- Added function keyword arguments like in python. Rationale: useful.
- Added a exclusive or
xor
operator. Rationale: logic completeness. - Binary logic is callable. Ratioanle: useful.
- Allow splicing like in CoffeeScript. Rationale: easier transition, useful.
- Overloaded equals when using regex to mean exec/test. Rationale: shorter.
- Static functions are inherited. Rationale: as in CoffeeScript - ease transition.
- Removed the constructor shorthand. Rationale: rarely useful, use it for cascade instead.
- Changed cascade op from
&
to..
. Rationale: like in other languages. - Added match statement and deep compare. Rationale: experimental, will see if good.
- For changes after 1.0.0, see the changelog section.