CoffeeScript, Dart, Elm, and TypeScript

Script Mods

© Lead Image © Daniil Peshkov, 123RF.com

© Lead Image © Daniil Peshkov, 123RF.com

Article from Issue 202/2017
Author(s):

JavaScript is the stuff of which many interactive web clients is made, but it comes with a fair amount of historical ballast. The creators of four alternative scripting languages seek to ditch the ballast.

Browser-based interactive applications have helped the recent comeback of JavaScript, although the scripting language first saw the light of day in the mid-1990s. Over the course of time, JavaScript's makers added more and more new constructs to what was initially a fairly simple scripting language. One prime example is prototype-based object orientation, hated by many developers and often misunderstood and unused.

Not until 2015 was the scripting language, by then standardized as ECMAScript, given optional class-based object orientation. However, compared with Java, C++, and others, it still lacks certain features [1]; moreover, neither JavaScript nor ECMAScript offers type checking, which continually results in exception errors in practice.

For this reason, several scripting languages want to replace JavaScript – or at least simplify it in terms of programming. Some of the most widespread and popular examples are CoffeeScript, Google's Dart, Elm, and Microsoft's TypeScript. In a comparison, I describe what distinguishes the four candidates and in which areas they are superior to JavaScript.

CoffeeScript

Jeremy Ashkenas published the first version of CoffeeScript [2] in 2009. His scripting language does not simply reinvent the wheel; rather, it supplements JavaScript with additional constructs so that developers can continue to use existing JavaScript libraries such as jQuery [3]. When this issue went to press, CoffeeScript had reached version 1.12.4, which only supported ECMAScript (ES) 5 and some parts of ES2015. The upcoming CoffeeScript 2.0.0 will implement more elements from ES2015 – in particular, the classes it introduces.

The compiler is available under the MIT license and requires a JavaScript run-time environment (e.g., Node.js). CoffeeScript plugins exist for many text editors, such as Emacs and Gedit. If you want to try CoffeeScript first, you can do so from an online editor [4] on the project site (Figure 1).

Figure 1: CoffeeScript website developers can type source code on the left in the Try CoffeeScript tab and then press Run to execute. The compiled JavaScript code appears on the right.

A compiler provided by the CoffeeScript team translates CoffeeScript code into the JavaScript equivalent, which in turn runs in any browser. The JavaScript code is said to run faster than an equivalent written by hand.

CoffeeScript also lets you use shorthand notation. Among other things, you can leave out the var before variable declarations and the semicolon at the end of an expression. Comments begin with a hashtag (#), as in shell scripts, whereas two slashes (//) cause integer division. Longer strings and regular expressions can run across multiple lines. CoffeeScript also lets you chop up arrays:

start = countdown[0..2]

In this case, start only contains the first three items of countdown[]. You can create functions with the -> operator and set the parameters to default values:

mult = (x = 1) -> x * x

Rather than curly braces, indentations mark statement sections, as in Python. Listing 1 defines a Dot object in this way with two properties and one method, draw(). The splat operator (...) lets you pass any number of parameters to a function. The ? checks whether a variable exists.

Listing 1

Simple Object in CoffeeScript

 

The pattern <result> = <value> if <condition> exists for simple conditions. Because the JavaScript comparison operator (==) often leads to misinterpretations, the CoffeeScript compiler automatically translates it to ===. Additionally, many Boolean and comparison operators have synonyms; for example, on and off are identical to true and false. Chained comparisons quickly test whether a variable lies in a particular range:

inpicture = 0 > Dot.x > 640

The scripting language also offers a Ruby-style switch-case construct.

Besides this, CoffeeScript extends for loops to include comprehensions, which help the code iterate through the elements of an array in a particularly elegant way. At the same time, they are expressions and can be assigned and returned:

drive = (a) -> alert(a)
drive auto for auto in ['bmw', 'vw',  'skoda'] when auto is not 'skoda'

The compiler also attempts to transform statements into expressions. Functions always return their last value, which means you can often omit return. A while loop also contains an array with the result of one iteration of a loop. In the example, countdown contains an array with the numbers 9 to  :

counter = 10
countdown = while counter > 0
   counter = counter - 1

CoffeeScript 1.12.4 introduced a simple class concept (Listing 2). Here, @ is the short form of this; super (line 8) invokes the function currently running in the parent class. The upcoming CoffeeScript 2.0.0 compiles the classes to create their counterparts from ES2015.

Listing 2

CoffeeScript Class Definition

 

CoffeeScript 1.12.4 already supported generator functions and tagged template literals from ES2015. Template literals incorporate the contents of variables into text:

greeting = "Hello #{name}"

In addition to the compiler, the CoffeeScript package includes a simple build system that works much like Make or Rake. Its core is a Cakefile, in which you define the various tasks for the compiler. These can then be executed using the cake tool. You then feed a markdown document with the .litcoffee extension to the CoffeeScript compiler. The compiler interprets all the indented blocks in the document as CoffeeScript code and ignores the rest.

Dart

Google first presented its Dart [5] scripting language to the general public in 2011. Either a Dart virtual machine (VM) executes programs written in Dart, or a compiler converts them into JavaScript code. The Dart SDK provided by Google includes the Dart VM and compiler; it is available under a BSD-style license and includes other useful tools. For example, dartanalyzer analyzes the Dart scripts fed to it and points out potential pitfalls. Additionally, a Chromium browser comes with an integrated Dart VM [6] dubbed Dartium.

Plugins help IDEs and editors such as Atom [7] and Emacs [8] learn the scripting language; WebStorm [9] even supports Dart out of the box. If you do not want to install the SDK, you can get started with the DartPad [10] online editor shown in Figure 2. According to Google, Dart is especially suited for programming larger applications. ECMA has now standardized Dart [11].

Figure 2: You can easily try out Dart scripts in DartPad. At bottom right, you can see the compiler notes.

Each Dart program must have a main() function as its entry point. Figure 2 on the left shows an example of a simple script that defines a sayHello() function, which main() calls. If you indicate the variable type, as in this example, a type test is performed later on. In addition to floating-point numbers (double) are integers (int). Both are subtypes of num, which supports the basic addition (+), subtraction (-), multiplication (*), and division (/) operations.

Dart also supports strings (String) and Boolean values (bool). A long string can be spread across multiple lines. Strings use UTF-16 encoding by default. If you want a variable to store arbitrary values, you declared it with var, as in JavaScript. Dart uses the $ notation to add variable content to a string, and ${exp} lets you integrate an entire expression (exp) into the text.

For short functions like sayHello(), you can use shorthand notation:

String sayHello(String name) => 'Hello  $name.';

The enum type lets you create a custom data type. In the following example, variables of type Color assume one of three values: Color.Red, Color.Yellow, or Color.Blue:

enum Color {Red, Yellow, Blue}

Dart only supports the == comparison operator, with shorthand notations for conditions:

<C>var result = condition? Expr1 : expr2<C>

or, even shorter,

<C>var result = expr1 ?? expr2<C>

In the latter case, if expr1 is not null, Dart returns its value; otherwise, it returns the result of expr2.

If Dart expects a Boolean value, only true is evaluated as true. Unlike JavaScript code, the following thus returns nothing:

var smith = 'Mr Smith';
If (smith) print('Hi!');

In addition to arrays – referred to as lists – Dart has maps, which store key-value pairs. In addition to the for loops known from JavaScript, programmers can use foreach() to iterate over the elements of an object and forin to iterate through lists and other iterable objects. To leave a while loop, you use break; continue immediately starts the next iteration of the loop. Both keywords also occur in switch-case constructs.

In Dart, everything is an object. Functions store variables, which means that variables can also be used as arguments for other functions. As in CoffeeScript, you can denote parameters as optional and define default values. Dart supports anonymous (lambda) functions and closures. Types can be tested at run time with the keywords as, is, and is! (i.e., is not); for example:

(p as Person).name = 'Henry';

Listing 3 shows an example of a class definition: The constructor in Dart has the same name as the class. The example also uses shorthand notation that directly assigns the values passed to x and y. Alternatively, the variables can be listed, wherein the assignment occurs before the constructor body is executed:

Dot(num a, num b) : x=a, y=b { ... }

Listing 3

Class Definition Example in Dart

 

If needed, you can name constructors and thus highlight their purpose, as shown in Listing 3 in Dot.onXAxis(). The attached : this(x,0) (line 6) calls the nameless constructor. Unlike CoffeeScript, Elm, and TypeScript, you can overload some operators (e.g., +). In the listing, this ability is used to add two more dots later.

Inheritance is implemented with extends, and super accesses the parent class. All methods and properties are public. If their names start with an underscore (e.g., _coverage in this example), they are automatically only visible within the class (private).

The Square class in Listing 3 demonstrates an abbreviated notation for get and set methods. Additionally, you can create constant objects that are immutable in the course of the program. Properties and methods marked as static are shared by all objects in a class.

Factory constructors are used to ensure that only a single or a specific object of the class exists; mixins let you combine multiple classes in a single class:

class Car extends Engine with Body { //... }

In Listing 3, if you replaced extends with implements, Square would inherit the interface from Dot, but not the implementations. Dart can derive classes from those classes tagged as abstract but cannot create any objects.

If necessary, you can initiate access to non-existing methods in Dart. To do so, you only need to implement the appropriate class method:

noSuchMethod(Invocation mirror)

If the class provides a method named call(), both its object and its function can be called.

Derived classes can override the methods of the parent class with their own versions. To do this, you need to tag them with @override:

class Rectangle extends Dot {
        @override
        void draw() { ... }
}

Besides @override, Dart currently supports other annotations – known as metadata. Dart developers should no longer use methods tagged as @deprecated. Finally, objects can be produced from JSON data:

var dot = JSON.decode('{"x":1, "y":2}');

Dart also supports generics. When declaring a class or function, it is not clear which values one of its objects will process later; thus, you need to use a placeholder (e.g., T in the example that follows). You only define the type when creating the object; extends (in the angle brackets) lets you assign types to specific (sub)classes:

class Car<T extends Daimler> {
        T drive(T model) { ... };
}
var mb = new Car<Mercedes>();

Dart is oriented on Java when it comes to handling exceptions; for example, you use throw to create an object and catch to field it. Dart provides prebuilt error and exception classes for error handling, from which you can derive your own classes.

Dart supports asynchronous programming: A function tagged async returns immediately before Dart has run the statements contained in the function's body. Similarly, a function call tagged await waits until the asynchronous function has finished its work. The future API will offer a concept similar to Promise objects in ES2015.

Dart code can be outsourced to libraries, if needed, with each library using its own namespace. All functions and variables that start with an underscore in a library are visible and can be used only within the corresponding library. The import statement integrates libraries, which can reside on other servers, into a Dart script.

Dart even supports lazy loading, in which the Dart program does not load the library until it actually needs it. Existing JavaScript libraries can only be addressed via an API.

Elm

Evan Czaplicki created Elm [12] in 2012; the Elm Software Foundation is now responsible for development. In contrast to the other alternatives, Elm is a functional programming language. Although a compiler translates the code written in Elm to JavaScript code, existing JavaScript libraries can only be used with a special interface.

The compiler is available under a BSD-style license and is written in Haskell. It not only points out errors, it also delivers solutions (friendly error messages). As an alternative to the compiler, you can use a special console (a REPL, or Read-Eval-Print Loop shell), that directly executes the Elm code you type. Plugins are available for several popular editors, such as Atom, IntelliJ IDEA [13], and Emacs. If you are interested, you can try out Elm in an online editor [14].

Elm itself only supports a few constructs and keywords. When you define a variable, you can also specify the type. Elm distinguishes between Booleans, integers, floating-point numbers, characters, and strings:

counter: int
counter = 42

The compiler automatically infers the type of the variable and warns of problems. The ++ operator lets Elm developers weld strings together. The language distinguishes between integer division (/) and floating point division (//). Comments start with --. Lists are similar to JavaScript arrays:

colors = ["Red", "Blue", "Yellow"]

Lists can be manipulated with functions from a supplied library. For example, List.sort colors sorts the colors list. Tuples, such as ("Henry", 32) contain a fixed number of random values. Records take several variables; the dot operator points to the value of a variable:

dot = { x = 1, y = 2 }
dot.x

Alternatively, you can use .x dot to tell Elm to retrieve the variable x from the dot record. To modify the values in a record, use {dot | x = 3}. All record manipulations are non-destructive: Elm does not change the value of x in the dot record but generates a completely new record. The compiler ensures efficient use of memory. In functions, you use the record elements directly:

dist {x,y} = sqrt (x^2 + y^2)

Finally, union types correspond to enum in Dart. In the following example, the new Color data type is created, from which variables can assume the value red, yellow, or blue.

type Color = Red | Yellow | Blue

Functions can be defined without the usual brackets, which are also missing in calls. You only need to use spaces to separate several parameters:

square n = n * n

If you want to avoid calling nested functions, you can use the pipe operator (|) to write them one after another. Elm supports anonymous, or lambda, functions, such as: \n -> n/2. The \ is followed by the name of the parameter, and the arrow is followed by the function part. Conditions are also short and sweet:

isPositive number = if number > 0 then "Positive" else "Not positive"

Alternatively, you can use caseof (as shown in Figure 3), and you can limit the visibility of variables to a specific expression with letin. This essentially describes the language scope. The built-in functions let you design a complete web application and support access to HTML code. They consistently use a model-view-controller concept, which is referred to as the Elm architecture (Figure 3).

Figure 3: Elm provides several ready-made examples in the online editor. For example, the buttons example selected here creates the two buttons on the right side, with which users can change the number displayed.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • CoffeeScript

    Through the years, many languages have tried to improve or even replace JavaScript, including CoffeeScript, which runs in all JavaScript Runtime Environments and compiles into JavaScript.

  • Google Dart

    The Dart programming language is Google’s modern alternative to JavaScript. It will mainly run in browsers but can also be used at the command line and on servers as a replacement for PHP.

  • Google Web Toolkit

    The Ingenious Google Web Toolkit builds optimized JavaScript applications in a hurry.

  • MathLex

    MathLex lets you easily transform handwritten math formulas to digital format and use them on the web.

  • Helma

    The powerful Helma application server brings new powers to the JavaScript language. We'll show you how to use Helma to build a simple RSS reader.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News