Google Dart
Google’s JavaScript Alternative
ByThe 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.
Code written in Google’s Dart programming language looks very much like a mixture of JavaScript and Java seasoned with a pinch of Scala. This resemblance is intentional: The programming language was designed on the basis of the wish to eliminate some problems and legacy ballast from JavaScript. The idea was that Dart programs would execute more quickly, provide better security, work on any Internet-capable device, and be suitable for even larger projects. If you have experience with the languages referred to – and this description fits almost any web programmer – you will quickly feel at home in Dart.
Programs written in Dart will run in a special virtual machine in the browser. The Dart project provides a reference implementation on its website. The implementation is even capable of executing Dart programs at the command line or on an Internet server. Dart is thus principally a suitable replacement for PHP. Besides the virtual machine, the project offers a couple of libraries whose functions – among other things – support convenient manipulation of a website’s DOM tree. A couple of other tools also make life easy for programmers, such as the Eclipse-based Dart editor (Figure 1).
Work in Progress
All of this sounds very enticing, with one tiny drawback: Dart is currently at a very early stage of development and thus is not suitable for production use. The inventors of Dart deliberately released their specification at an early stage to collect as many suggestions for improvements as possible and to improve acceptance. This is also precisely why the language specification has been placed under the Creative Commons Attribution 3.0 license and the program code published by the project under the Google BSD License. When this article was written, the latest version of Dart was 0.08, and this is the version on which the following overview is based.
main() and Classes
The starting point for a Dart program, as in Java, is main(). For JavaScript programmers, especially, this is likely to be new:
main() { // A small comment print("Hello World!"); }
JavaScript programmers will be coughing again: Dart is an object-oriented language across the board. Listing 1 shows the declaration of a class named Car.
Listing 1: A Simple Class
01 class Car { 02 var color; 03 04 Car() { 05 this.color = "blue"; 06 } 07 08 Car.paint(this.color); 09 } 10 11 main() { 12 Car ferrari = new Car.paint("red"); 13 Car bmw = new Car(); 14 print(ferrari.color); // outputs red 15 print(bmw.color); // outputs blue 16 }
The class starts by defining a new variable color. As the keyword var suggests, variables can take on arbitrary values at any time – like JavaScript, Dart is a typeless language. This is followed by the constructor Car(), which must use the same name as the class. this stands for the current object, as in Java; in other words, this.color ensures that the value doesn’t end up in a global or new variable.
Line 8 shows two features of Dart. In typed languages like Java, the developer can define multiple constructors with different parameters (overloading). When an object is created, the language then automatically calls the matching constructor. In typeless languages like Dart, this choice is not so simple. To solve this problem, the language uses known constructors. In Listing 1 the second constructor is called paint, and if you want to call precisely this constructor when creating an instance of Car in the main() function, you need to specify the name there:
Car ferrari = new Car.paint("red");
In contrast, new Car() would just call the normal constructor.
As a second special feature, the constructor, Car.paint() uses an abbreviation: Dart assigns the value passed into it directly to the color variable. Of course, if you prefer, you can use the long version:
Car.paint(var onecolor) { this.color = onecolor; }
Variables that can assume arbitrary values might be very convenient, but this approach does tend to cause some laxity on the part of programmers in the real world, which leads to errors that could be difficult to troubleshoot. For this reason, programmers can optionally specifying a type for a variable:
class Car { String color; [...]
If the Dart program does try to store a number in color at some later stage of the program, the virtual machine creates a warning. As I said, this is just a warning, not an error – the program will continue to run with a number in color. This behavior draws a programmer’s attention to assignment errors and makes the code more legible, and users do not need to fear sudden program crashes. Besides the String type, Dart also supports the types listed in Table 1.
Incidentally, all of these are objects internally. You can even create lists and maps with new:
var autos = new List();
Variables that have not yet been assigned a value automatically have the special value null.
Like PHP, Dart can insert the content of variables into the strings. The code
String name = "John Doe"; print ("Hello ${name}");
would output Hello John Doe. Inside the brackets {} you can even have complete expressions or function calls.
Line Version
Dart users can abbreviate an individual function definition like the following:
int square(int number) { return number*number; }
with =>
int square(int number) => number*number;
(e.g., => e stands for { return e; }). In Dart, you can pass one function to another, which is very useful in for loops:
sayHello(String name) => print("Hello ${name}"); List names = ["Tim", "Joe", "Henry"]; names.forEach(sayHello);
The sayHello function writes the string passed in to it after Hello and outputs the results onscreen. The second line creates a list with three names which, in turn, are pushed one after another into the function handed into it by forEach – in this case, the function is sayHello(). In other words, this three-line code snippet outputs Hello Tim, Hello Joe, and Hello Henry.
Because sayHello only occurs in names.forEach(), you can declare the function there directly and output their names in the same way:
List names = ["Tim", "Joe", "Henry"]; names.forEach( (String name) => print("Hello ${name}") );
This compact form of coding works well here, but in more complex functions, you could easily lose track of what you’re doing.
The usual suspects are available for control flow: for, if, switch, and while, and they act just like their counterparts in Java or JavaScript. Additionally, you can use exceptions to catch errors.
Classic
Dart inherited interfaces from Java. When a class implements an interface, it guarantees the function specified in the interface is provided. Listing 2 provides an inheritance example.
Listing 2: interface and extends
01 interface Surface { 02 int surface_content(); 03 } 04 05 class Square implements Surface { 06 int width; 07 Square(int this.width); 08 int surface_content() => width*width; 09 } 10 11 class Rectangle extends Square { 12 int _height; 13 Rectangle(int this._height, int br) : super(br); 14 int surface_content() => width * _height; 15 } 16 17 main() { 18 Square q = new Square(3); 19 Rectangle r = new Rectangle(2,3); 20 print(q.surface_content()); 21 print(r.surface_content()); 22 }
A class can implement multiple interfaces but only inherits from exactly one class (i.e., uses extends to extend the class). In Listing 2, all of the variables and functions are public, with one exception, _height. If the name of the variable or a function starts with an underscore, it is private, and you can only access the variable or function from within the class itself. In Listing 2, this means you cannot retroactively modify the height of a rectangle.
Factory Sales
Design patterns have been part of the professional developer’s repertoire for many years. It thus comes as no surprise that Dart also supports this feature. For example, the Factory Pattern is already part of the language: If you prefix the constructor with the factory keyword, Dart doesn’t automatically create an object of this class but leaves it to the constructor. The programmer can then look in a cache to see whether a matching object already exists, as you can see in Listing 3.
Listing 3: Factory Pattern
01 class Car { 02 String madeby; 03 static Map garage; 04 05 factory Car(String manufacturer){ 06 if(Car.garage == null) Car.garage=new Map(); 07 08 if (Car.garage.containsKey( manufacturer)!=null) return Car.garage[manufacturer]; 09 else { 10 Car newcar = new Car.buy(manufacturer); 11 Car.garage[manufacturer] = newcar; 12 return newcar; 13 } 14 } 15 16 Car.buy(this.madeby); 17 } 18 19 main() { 20 var onecar = new Car("Ferrari"); 21 var anothercar = new Car("Ferrari");
The anothercar variable in this example points to the same object as onecar. The static keyword ensures that garage exists only once; all Car objects access the same variable.
The Factory Pattern can also be combined with interfaces. To do this, you assign the standard factory class to an interface, and the factory class returns objects to match the interface. Listing 4 shows an example of this.
Listing 4: Combining interface and factory
<sub>01 interface Car default CarFactory { 02 Car(manufacturer); 03 final manufacturer; 04 } 05 06 class CarFactory { 07 factory Car(manufacturer) { 08 if (manufacturer == "Ferrari") { 09 return new Racingcar(manufacturer); 10 } 11 return new Saloon(manufacturer); 12 } 13 } 14 15 class Racingcar implements Car { 16 Racingcar(this.manufacturer); 17 String manufacturer; 18 } 19 20 class Saloon implements Car { 21 Saloon(this.manufacturer); 22 String manufacturer; 23 } 24 25 main() { 26 print(new Car("Ferrari") is Racingcar); 27 print(new Car("VW") is Racingcar); 28 }</sub>
The interface defines the signature of the constructor, which then outputs objects to match the interface. CarFactory in Listing 4 will return either a Racingcar or Saloon object depending on the manufacturer that is passed into it. During instantiation in main(), it looks as though you are creating a Car directly.
Additionally, the is keyword tests to see whether an object is a specific type. Incidentally, the final keyword in front of manufacturer ensures that the variable can only be assigned a value precisely once during its initialization.
Container Service
Dart contains generic types, also known as generics, which you will probably be familiar with from Java. C++ programmers are also familiar with this concept as templates. You can use them to create containers quickly for arbitrary objects. The built-in lists and maps are generics. For example, List<Car> creates a list of Car objects:
main() { List<Car> carlist = new List<Car>(); carlist.add(new Car("Ferrari")); Car onecar = carlist[0]; }
Because variables can assume arbitrary content, Dart will not disallow this:
List<Car> carlist = new List<Car>(); List<LKW> trucklist = carlist;
That said, this experiment might blow up later if the developer tries to do something with what they assumed to be a truck from the trucklist.
Parallelism
Programmers normally shift tasks that need to run in parallel into separate threads and then put much hard work into merging the intermediate results. Dart removes the headaches from this process: An object derived from the basic Isolate class will, if so desired, run separately from the main program in its own thread.
To be able to exchange intermediate results, Isolates can send each other messages. These messages are first queued until the receiving isolate picks up from what is known as a port. This model is very reminiscent of the actual model from Erlang or Scala. Listing 5 shows a complete example.
Listing 5: Communicating with an Isolate
01 class Receiver extends Isolate { 02 main() { 03 port.receive((message, replyTo) { 04 if (message == null) port.close(); 05 else print("Receiving: ${message}"); 06 }); 07 } 08 } 09 10 main() { 11 new Receiver().spawn().then((port) { 12 for (var message in ['This', 'is', 'a', 'test']) { 13 port.send(message); 14 } 15 port.send(null); 16 }); 17 }
The sample program first receives a new Receiver object, which it dumps into a separate thread with spawn(). After this, it sends the object four words in succession. As soon as the object has received a word, it writes it to the screen. The Isolate is always given a port to the object that sent the message in the form of replyTo. This means it can reply directly to its caller.
Isolates run in separate memory space. One positive side effect of this is that garbage collection can handle each Isolate individually.In return, the virtual machine needs to copy the messages between the Isolate. In the future, Isolates will even be able to use different Dart libraries with different versions.
DOM Access
Because Dart was designed to replace JavaScript, programmers must be able to use the language to access the DOM tree of a website. For this to happen, Dart includes its own library which you can import as follows:
#import("dart:html");
Then, you can use the following expression to access a <div> with the ID menu:
document.query("#menu");
Elements can thus be located using CSS selectors in a style very similar to jQuery. Dart programs can use other classes and functions to set up HTTP connections (keyword Ajax), process JSON data, and even access the filesystem, among other things. For a complete list, read the API Reference.
Executed
Because today’s browsers currently don’t understand Dart programs, the Dart project has developed the Dartc compiler, which converts Dart programs into JavaScript code. However, Dartc had the reputation of creating extremely large JavaScript programs that ran extremely slowly. To solve this problem, a GNU compiler named Frog was created (and even written in Dart) that generates far more compact JavaScript code. Frog and a virtual machine for the command line (Figure 2) are included in the Dart SDK, which is available free of charge from the project homepage.
All you need to do is download the matching ZIP archive, unpack, and then either compile your own Dart program (e.g., test.dart) using Frog from the bin directory,
./frogc --enable_type_checks test.dart
or run the program directly in the virtual machine:
./dart --enable_type_checks test.dart
The --enable_type_checks parameter ensures that the compiler, or the virtual machine, enables type verification.
If you prefer not to use the SDK right now, you can run your own programs directly on the Dart project’s website on what is known as the Dartboard (Figure 3).
Dartboard integrates most of the language’s libraries out of the box (with the exception of dart:html), whereas programmers need to use the import statement if they work with the command-line virtual machine.
Finally, you could also use Dartium, a special version of the Chromium browser with a built-in Dart virtual machine. You can check out the complete specification of the Dart language and the source code of a couple of larger Dart sample programs at the dartlang.org website.
The Future
With the exception of Dartium, all of today’s browsers currently ignore Dart, and you can’t blame them: After all, the programming language is not nearly complete. At the same time, Dart is facing heavy criticism: Most observers feel that supporting another web language is superfluous and counterproductive.
The future of Dart will depend to a great extent on how seriously Google is taking it. Google has the power to assert this language, and if it makes the grade for Android devices and the Chrome browser, other browser vendors will not be able to look away. However, the danger is that a community of Dart opponents could come into existence, and this would again mean fragmentation of the web.
That said, even critics must admit that Dart programs are easier to read and understand than their JavaScript counterparts. Thanks to genuine object orientation, larger and more easily maintainable web applications are also possible. The prospect of a single language for the client and the server is also enticing – web programmers would no longer need to switch back and forth between JavaScript and, say, PHP. But even if you don’t come from the Java camp, you should be able to find your feet in a very short time. Finally, the language is organized in an open way, in contrast with Java, and any user can submit proposals for improvements. At least, this has been the process thus far.
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.
News
-
Armbian 24.11 Released with Expanded Hardware Support
If you've been waiting for Armbian to support OrangePi 5 Max and Radxa ROCK 5B+, the wait is over.
-
SUSE Renames Several Products for Better Name Recognition
SUSE has been a very powerful player in the European market, but it knows it must branch out to gain serious traction. Will a name change do the trick?
-
ESET Discovers New Linux Malware
WolfsBane is an all-in-one malware that has hit the Linux operating system and includes a dropper, a launcher, and a backdoor.
-
New Linux Kernel Patch Allows Forcing a CPU Mitigation
Even when CPU mitigations can consume precious CPU cycles, it might not be a bad idea to allow users to enable them, even if your machine isn't vulnerable.
-
Red Hat Enterprise Linux 9.5 Released
Notify your friends, loved ones, and colleagues that the latest version of RHEL is available with plenty of enhancements.
-
Linux Sees Massive Performance Increase from a Single Line of Code
With one line of code, Intel was able to increase the performance of the Linux kernel by 4,000 percent.
-
Fedora KDE Approved as an Official Spin
If you prefer the Plasma desktop environment and the Fedora distribution, you're in luck because there's now an official spin that is listed on the same level as the Fedora Workstation edition.
-
New Steam Client Ups the Ante for Linux
The latest release from Steam has some pretty cool tricks up its sleeve.
-
Gnome OS Transitioning Toward a General-Purpose Distro
If you're looking for the perfectly vanilla take on the Gnome desktop, Gnome OS might be for you.
-
Fedora 41 Released with New Features
If you're a Fedora fan or just looking for a Linux distribution to help you migrate from Windows, Fedora 41 might be just the ticket.