# Just like its CoffeeScript cousin, LiveScript uses number symbols for # single-line comments. /* Multi-line comments are written C-style. Use them if you want comments to be preserved in the JavaScript output. */ # As far as syntax goes, LiveScript uses indentation to delimit blocks, # rather than curly braces, and whitespace to apply functions, rather # than parenthesis. ######################################################################## ## 1. Basic values ######################################################################## # Lack of value is defined by the keyword `void` instead of `undefined` void # same as `undefined` but safer (can't be overridden) # No valid value is represented by Null. null # The most basic actual value is the logical type: true false # And it has a plethora of aliases that mean the same thing: on; off yes; no # Then you get numbers. These are double-precision floats like in JS. 10 0.4 # Note that the leading `0` is required # For readability, you may use underscores and letter suffixes in a # number, and these will be ignored by the compiler. 12_344km # Strings are immutable sequences of characters, like in JS: "Christina" # apostrophes are okay too! """Multi-line strings are okay too.""" # Sometimes you want to encode a keyword, the backslash notation makes # this easy: \keyword # => 'keyword' # Arrays are ordered collections of values. fruits = * \apple * \orange * \pear # They can be expressed more concisely with square brackets: fruits = [ \apple, \orange, \pear ] # You also get a convenient way to create a list of strings, using # white space to delimit the items. fruits = <[ apple orange pear ]> # You can retrieve an item by their 0-based index: fruits[0] # => "apple" # Objects are a collection of unordered key/value pairs, and a few other # things (more on that later). person = name: "Christina" likes: * "kittens" * "and other cute stuff" # Again, you can express them concisely with curly brackets: person = {name: "Christina", likes: ["kittens", "and other cute stuff"]} # You can retrieve an item by their key: person.name # => "Christina" person["name"] # => "Christina" # Regular expressions use the same syntax as JavaScript: trailing-space = /\s$/ # dashed-words become dashedWords # Except you can do multi-line expressions too! # (comments and whitespace just gets ignored) funRE = // function\s+(.+) # name \s* \((.*)\) \s* # arguments { (.*) } # body // ######################################################################## ## 2. Basic operations ######################################################################## # Arithmetic operators are the same as JavaScript's: 1 + 2 # => 3 2 - 1 # => 1 2 * 3 # => 6 4 / 2 # => 2 3 % 2 # => 1 # Comparisons are mostly the same too, except that `==` is the same as # JS's `===`, where JS's `==` in LiveScript is `~=`, and `===` enables # object and array comparisons, and also stricter comparisons: 2 == 2 # => true 2 == "2" # => false 2 ~= "2" # => true 2 === "2" # => false [1,2,3] == [1,2,3] # => false [1,2,3] === [1,2,3] # => true +0 == -0 # => true +0 === -0 # => false # Other relational operators include <, <=, > and >= # Logical values can be combined through the logical operators `or`, # `and` and `not` true and false # => false false or true # => true not false # => true # Collections also get some nice additional operators [1, 2] ++ [3, 4] # => [1, 2, 3, 4] 'a' in <[ a b c ]> # => true 'name' of { name: 'Chris' } # => true ######################################################################## ## 3. Functions ######################################################################## # Since LiveScript is functional, you'd expect functions to get a nice # treatment. In LiveScript it's even more apparent that functions are # first class: add = (left, right) -> left + right add 1, 2 # => 3 # Functions which take no arguments are called with a bang! two = -> 2 two! # LiveScript uses function scope, just like JavaScript, and has proper # closures too. Unlike JavaScript, the `=` works as a declaration # operator, and will always declare the variable on the left hand side. # The `:=` operator is available to *reuse* a name from the parent # scope. # You can destructure arguments of a function to quickly get to # interesting values inside a complex data structure: tail = ([head, ...rest]) -> rest tail [1, 2, 3] # => [2, 3] # You can also transform the arguments using binary or unary # operators. Default arguments are also possible. foo = (a = 1, b = 2) -> a + b foo! # => 3 # You could use it to clone a particular argument to avoid side-effects, # for example: copy = (^^target, source) -> for k,v of source => target[k] = v target a = { a: 1 } copy a, { b: 2 } # => { a: 1, b: 2 } a # => { a: 1 } # A function may be curried by using a long arrow rather than a short # one: add = (left, right) --> left + right add1 = add 1 add1 2 # => 3 # Functions get an implicit `it` argument, even if you don't declare # any. identity = -> it identity 1 # => 1 # Operators are not functions in LiveScript, but you can easily turn # them into one! Enter the operator sectioning: divide-by-two = (/ 2) [2, 4, 8, 16].map(divide-by-two) .reduce (+) # Not only of function application lives LiveScript, as in any good # functional language you get facilities for composing them: double-minus-one = (- 1) . (* 2) # Other than the usual `f . g` mathematical formulae, you get the `>>` # and `<<` operators, that describe how the flow of values through the # functions. double-minus-one = (* 2) >> (- 1) double-minus-one = (- 1) << (* 2) # And talking about flow of value, LiveScript gets the `|>` and `<|` # operators that apply a value to a function: map = (f, xs) --> xs.map f [1 2 3] |> map (* 2) # => [2 4 6] # You can also choose where you want the value to be placed, just mark # the place with an underscore (_): reduce = (f, xs, initial) --> xs.reduce f, initial [1 2 3] |> reduce (+), _, 0 # => 6 # The underscore is also used in regular partial application, which you # can use for any function: div = (left, right) -> left / right div-by-two = div _, 2 div-by-two 4 # => 2 # Last, but not least, LiveScript has back-calls, which might help # with some callback-based code (though you should try more functional # approaches, like Promises): readFile = (name, f) -> f name a <- readFile 'foo' b <- readFile 'bar' console.log a + b # Same as: readFile 'foo', (a) -> readFile 'bar', (b) -> console.log a + b ######################################################################## ## 4. Patterns, guards and control-flow ######################################################################## # You can branch computations with the `if...else` expression: x = if n > 0 then \positive else \negative # Instead of `then`, you can use `=>` x = if n > 0 => \positive else \negative # Complex conditions are better-off expressed with the `switch` # expression, though: y = {} x = switch | (typeof y) is \number => \number | (typeof y) is \string => \string | 'length' of y => \array | otherwise => \object # `otherwise` and `_` always matches. # Function bodies, declarations and assignments get a free `switch`, so # you don't need to type it again: take = (n, [x, ...xs]) --> | n == 0 => [] | _ => [x] ++ take (n - 1), xs ######################################################################## ## 5. Comprehensions ######################################################################## # While the functional helpers for dealing with lists and objects are # right there in the JavaScript's standard library (and complemented on # the prelude-ls, which is a "standard library" for LiveScript), # comprehensions will usually allow you to do this stuff faster and with # a nice syntax: oneToTwenty = [1 to 20] evens = [x for x in oneToTwenty when x % 2 == 0] # `when` and `unless` can be used as filters in the comprehension. # Object comprehension works in the same way, except that it gives you # back an object rather than an Array: copy = { [k, v] for k, v of source } ######################################################################## ## 4. OOP ######################################################################## # While LiveScript is a functional language in most aspects, it also has # some niceties for imperative and object oriented programming. One of # them is class syntax and some class sugar inherited from CoffeeScript: class Animal (@name, kind) -> @kind = kind action: (what) -> "*#{@name} (a #{@kind}) #{what}*" class Cat extends Animal (@name) -> super @name, 'cat' purr: -> @action 'purrs' kitten = new Cat 'Mei' kitten.purr! # => "*Mei (a cat) purrs*" # Besides the classical single-inheritance pattern, you can also provide # as many mixins as you would like for a class. Mixins are just plain # objects: Huggable = hug: -> @action 'is hugged' class SnugglyCat extends Cat implements Huggable kitten = new SnugglyCat 'Purr' kitten.hug! # => "*Mei (a cat) is hugged*"