CoffeeScript

From Bauman National Library
This page was last modified on 8 June 2016, at 21:06.
CoffeeScript
CoffeeScript.png
Paradigm Multi-paradigm: prototype-based, functional, imperative, scripting
Designed by Jeremy Ashkenas
Developer Jeremy Ashkenas, et al.
First appeared December 13, 2009; 12 years ago (2009-12-13)
License MIT License
Website coffeescript.org
Influenced
MoonScript, LiveScript

CoffeeScript is a compact high-level programming language that transcompiles into JavaScript code. CoffeeScript uses syntactic sugar in style of Ruby, Python, Haskell and Erlang to improve code readability and reduce its size. CoffeeScript allows programmers to write more compact code in comparsion with JavaScript.

Initially the language compiler was written in Ruby. In version 0.5 (that was released at February 21, 2010) the compiler has been implemented via CoffeeScript itself. The language was warmly received in Ruby community. Built-in support of CoffeScript has been added to the web framework Ruby on Rails version 3.1.

Advantages over JavaScript

Today JavaScript is one of the most popular and common programming languages. It is typically used as an embeded language for software access to application objects. Most widely it is used in web-browsers as a scripting language to add interactivity to web-pages.

However, there are many developers who reject and criticize JavaScript, largely because of its illogical syntax and incompatible implementations. In terms of syntax, JavaScript is highly heterogeneous. For example, it shares many of the Scheme language ideas, but not its syntax - instead, JavaScript uses C-like syntax. The result is a language that borrows ideas of functional languages but with verbose syntax devoid of natural structures to express those ideas.

JavaScript is also an object-oriented and prototypical language. It is not based on classes as other languages used by many frameworks. Therefore, programming of applications in JavaScript can be very cumbersome.

CoffeeScript eliminates many of the shortcomings of its predecessor:

  • provides much simplified syntax with less ballast;
  • uses spaces to organize the code blocks;
  • provides simple syntax for expressions and functions;
  • provides class-based inheritance.

CoffeeScript is not an interpreted language and does not use a separate runtime library - all code is compiled into the corresponding code in Javascript. Thus, CoffeeScript gives the programmer a syntax that allows to use all the power of Javascript with minimal overhead.

Prerequisites

CoffeeScript is distributed as Node.Js package via its package manager NPM. Thus, to write and compile CoffeeScript code you must first install Node.Js. Runtime environment for JavaScript will also be needed. For this one can use the virtual machine JavaScript V8, which is a part of Node.Js).

CoffeeScript is installed like any other Node.Js package: npm install -g coffee-script

Compilation

To run the CoffeeScript compiler, simply enter the command coffee -c<filename>: e.g. coffee -ctest.coffee. Resulting .js-file will contain the corresponding JavaScript code. You can also specify the folder containing the source code instead of a single file: coffee --compile --output lib/ src/

Source file test.coffee

console.log "Hello world"

The resulting file test.js

(function() {
    console.log("Hello world");
}).call(this);

JavaScript supports scopes only at the function level. Because of this, the CoffeeScript compiler puts the code in an anonymous function, thereby ensuring that the variable will be limited with this function and won't become a global (overshadowing another global variable).

Syntax

Functions

Let's write simple functions for calculation square and cube of the number:

CoffeeScript JavaScript
square = (x) -> x * x
cube   = (x) ->
    square(x) * x
(function() {    
    var cube, square;

    square = function(x) {
        return x * x;
    };

    cube = function(x) {
        return square(x) * x;
    };
}).call(this);

All local variables (in this case - function names var cube, square) are declared at the beginning of the anonymous limiter-function. It helps to avoid a common error when the variable becomes global because the programmer forgot to declare it with var. However, a variable declared at higher level is visible to lower levels and can be changed there.

JavaScript keyword function is replaced with an arrow ->.

Keyword return is optional - it will be added automatically to the last expression in the function.

Blocks are formed with indentation (as in Python) instead of braces.

Conditional operators

CoffeeScript can compile if statements into the JavaScript expressions using ternary operator or bracket wrappers. CoffeeScript has no explicit ternary operator - instead it is allowed to use if as an expression.

CoffeeScript JavaScript
mood = greatlyImproved if singing

if happy and knowsIt
  clapsHands()
  chaChaCha()
else
  showIt()

date = if friday then sue else jill
var date, mood;

if (singing) {
  mood = greatlyImproved;
}

if (happy &amp;&amp; knowsIt) {
  clapsHands();
  chaChaCha();
} else {
  showIt();
}

date = friday ? sue : jill;

String formatting

CoffeeScript allows to format strings using variable names via #{name} constructions:

CoffeeScript JavaScript
for i in [0..5]
  console.log "Hello #{i}"
(function() {
  var i;
  for (i = 0; i <= 5; i++) {
    console.log("Hello " + i);
  }
}).call(this);

Default arguments

CoffeeScript JavaScript
fill = (container, liquid = "coffee") ->
    "Filling the #{container} with #{liquid}..."
var fill;
fill = function(container, liquid) {
  if (liquid == null) {
    liquid = "coffee";
  }
  return "Filling the " + container + " with " + liquid + "...";
};

The JavaScript implementation comes down to checking the parameter value. In case of equality to null or undefined the parameter is set to default value.

Comparsion operators

In JavaScript the == operator can behave unexpectedly, especially for a novice JS-programmer. Operator === is safer to use, so CoffeeScript automatically converts all the == operators to === operators. Nevertheless, there may be situations, where the == is needed: for example, when checking for value existence.

CoffeeScript JavaScript
alert "I knew it!" if elvis?
if (typeof elvis ! == "undefined" &amp;&amp; elvis ! == null) {
  alert("I knew it!");
}

The ? operator returns true unless a variable is null or undefined.

Loops and array comprehensions

The only low-level loop that CoffeeScript provides is the while loop. The main difference from JavaScript is that the while loop can be used as an expression, returning an array containing the result of each iteration through the loop. For readability, the until keyword is equivalent to while not, and the loop keyword is equivalent to while true.

CoffeeScript JavaScript
if this.studyingEconomics
  buy()  while supply > demand
  sell() until supply > demand
if (this.studyingEconomics) {
  while (supply > demand) {
    buy();
  }
  while (!(supply > demand)) {
    sell();
  }
}

Array comprehension (also "list comprehension") - is a syntactic construct available in some programming languages for creating a list based on existing lists.

CoffeeScript frequently uses array comprehensions, which are compiled into the standard for loops. Unlike JavaScript loops, array comprehension is an expression that can return values and be assigned. Comprehensions should be able to handle most places where you otherwise would use a loop, each/forEach, map, or select/filter.

CoffeeScript JavaScript
cubes = (math.cube num for num in list)
cubes = (function() {
  var i, len, results;
  results = [];
  for (i = 0, len = list.length; i < len; i++) {
    num = list[i];
    results.push(math.cube(num));
  }
  return results;
})();

To set the loop step one can use by keyword, for example: evens = (x for x in [0..10] by 2). Comprehensions can also be used to iterate over keys and values of an object. Keyword of allows to use comprehension over the properties of an object instead of the values in an array:

yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld

Array slices

Ranges can be used when working with arrays to extract slices of arrays. A range is inclusive if it is defined with two dots (..) and excludes the end if it is defined with three dots (...). Thus, the range of arr[3..6], includes the 3rd, 4th, 5th и 6th elements of the arr array. Range arr[3...6] excludes the last element: 3, 4, 5. Several indices have default values: omitted first index defaults to 0 and omitted second index defaults to the array size.

Slices can also be used for "block" replacements of several elements in the array.

CoffeeScript JavaScript
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
start   = numbers[0..2]
middle  = numbers[3...6]
end     = numbers[6..]
copy    = numbers[..]
numbers[3..6] = [-3, -4, -5, -6]
var copy, end, middle, numbers, start, _ref;

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
start = numbers.slice(0, 3);
middle = numbers.slice(3, 6);
end = numbers.slice(6);
copy = numbers.slice(0);
[].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref;

Embedded JavaScript

CoffeeScript allows to use snippets of pure JavaScript in its code. Those snippets should be put in ` quoutes:

CoffeeScript JavaScript
hi = `function() {
  return [document.title, "Hello JavaScript"].join(": ");
}`
var hi;

hi = function() {
  return [document.title, "Hello JavaScript"].join(": ");
};

Classes

On of the main disadvantages of JavaScript is its "unusual" style of object-oriented programming. In most cases OOP is based upon using of classes. In JavaScript, however, objects are often used as simple collections. Two styles of writing OOP code exist in JavaScript - the prototype style and the functional style. Moreover, there are a number of different libraries providing various means of "traditional" classes. These libraries may differ significantly from one another, introducing additional confusion to programmers.

CoffeeScript provides a basic class structure that allows you to name your class, set the superclass, assign prototypal properties, and define the constructor, in a single assignable expression.

class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5

class Horse extends Animal
  move: ->
    alert "Galloping..."
    super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

sam.move()
tom.move()

The extends operator is used to create an inheritance chain between classes; operator :: allows to refer to the prototype objects; the super() call is converted into a call to the ancestor class method with the same name.

The @name in constructor automatically defines the name property and assigns it a value passed by the constructor. When used in a method, @name - is a short form for this.name.

References