|
|
Subscribe / Log in / New account

A look at Dart

By John Coggeshall
July 29, 2020

Dart is a BSD-licensed programming language from Google with a mature open-source community supporting the project. It works with multiple architectures, is capable of producing native machine-code binaries, and can also produce JavaScript versions of its applications. Dart version 1.0 was released in 2013, with the most recent version, 2.8, released on June 3 (2.9 is currently in public beta). Among the open-source projects using Dart is the cross-device user-interface (UI) toolkit Flutter. We recently covered the Canonical investment in Flutter to help drive more applications to the Linux desktop, and Dart is central to that story.

Dart's syntax is a mix of concepts from multiple well-established languages including JavaScript, PHP, and C++. Further, Dart is a strongly-typed, object-oriented language, with primitive types that are implemented as classes. While Dart does have quirks, it is likely that a programmer familiar with the aforementioned languages will find getting started with Dart to be relatively easy. Included in the language are useful constructs like Lists (arrays), Sets (unordered collections), and Maps (key/value pairs).

Beyond the language constructs, the Dart core libraries provide additional support for features like asynchronous programming, HTML manipulation, and converters to work with UTF-8 and JSON.

Dart compilation targets

To learn about Dart's compilation features, we need something to compile; for purposes of an example, it was easy enough to write a small Dart application that calculates and displays the Fibonacci sequence recursively:

    /* Application entry point, just like C */
    void main() {
        int totalTerms = 45;
        String output = "";

        for(int i = 1; i <= totalTerms; i++) {
            output += fibonacci(i).toString() + ", ";
        }

        print(output + "...");
    }

    /* A compact syntax for the recursive fibonacci algorithm */
    int fibonacci(int n) => n <= 2 ? 1 : fibonacci(n - 2) + fibonacci(n - 1);

In Dart, like in C, the main() function is the entry point into the application. It is worth noting, however, the compact JavaScript-inspired syntax of the fibonacci() function. The function uses Dart arrow syntax combined with a conditional expression to implement the algorithm. For reference, below is a more verbose version of the same fibonacci() function:

    int fibonacci(int n) {
        if(n <= 2) {
            return 1;
        }

        return fibonacci(n - 2) + fibonacci(n - 1);
    }

The language provides many options for executing a Dart program. The most straightforward approach is from the command line using the dart command to execute the program using the Dart virtual machine (VM). Like other languages, Dart's VM implements just-in-time (JIT) compilation of Dart code into native machine code. Here's the output of the preceding example:

    $ dart fibonacci.dart
    1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610,
    987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368,
    75025, 121393, 196418, 317811, 514229, 832040, 1346269,
    2178309, 3524578, 5702887, 9227465, 14930352, 24157817,
    39088169, 63245986, 102334155, 165580141, 267914296,
    433494437, 701408733, 1134903170, ...

As an alternative to the VM, Dart can also be compiled into native machine code using the dart2native command. While it can accurately be described as compiling to native machine code, there are some differences when comparing Dart to other compiled languages, such as C. Because of the nature of the Dart language, not all code can necessarily be compiled to native machine code. There are multiple reasons for this; one example being that a Dart application may not be able to determine the type of a given variable in certain code blocks until runtime. In that case, those code blocks cannot take advantage of compilation and must be interpreted by the VM as bytecode. Thus, even compiled Dart applications still require a runtime in order to address those issues.

By default, dart2native will create a standalone binary of the application with the runtime bundled in. Alternatively, dart2native can create what is known as an ahead-of-time (AOT) binary. These binaries do not include an embedded runtime and cannot be executed directly. They can, however, be executed if a runtime environment is provided externally — such as by using the dartaotruntime command. Alternatively, AOT binaries can be used in Dart-powered cloud services where a runtime is provided by the service.

Finally, Dart can also "compile" to JavaScript (known as transpiling) using the dart2js command. The documentation indicates that the dart2js command is best suited for a production build, since the entire application is transpiled into a single optimized JavaScript file.

For development purposes, the documentation recommends the dartdevc command. Unlike dart2js, the dartdevc command supports incremental transpilation and produces a modular version of the application with multiple separate JavaScript files. This allows the developer to only transpile the changes in the application during development, saving considerable time otherwise wasted waiting to transpile the entire code base. This tool is often used in conjunction with a development web server that keeps the transpiled JavaScript up-to-date in real time as the underlying Dart code is edited. For developing browser-based Dart applications, Dart provides a development web server available through the webdev command. This tool handles rebuilding the application to JavaScript as it is being developed using dart2devc automatically.

Getting into Dart

To try Dart coding out yourself, there are a few options. Since Dart can be transpiled to JavaScript, the easiest approach is to simply use the project's provided browser-based runtime. For local installation of the SDK, Dart's project website provides instructions — including an APT package repository for Linux, Windows installation via Chocolatey, and macOS installation via Homebrew.

Beyond the Dart core libraries that ship with the language, there is an open-source community also providing libraries. The most prominent collection is a repository of packages called pub.dev that can be used in application development. To manage these libraries Dart uses a YAML-based dependencies file called pubspec.yaml stored in the project root directory. Below is an example pubspec.yaml for a project, where we define a dependency on the basic_utils package found on pub.dev:

    name: LWN-Example
    dependencies:
      basic_utils: 2.5.5

To download and install all of the dependencies for the application, Dart provides the pub command. All that is needed to resolve and install the dependencies on a system is to run pub get from the root directory of the project.

By default, these dependencies are assumed to come from the Dart pub.dev repository. For dependencies that are not available in that repository, Dart supports Git dependencies that allow them to be taken from Git repositories. Like the PHP Composer package tool, pub get produces a matching pubspec.lock file in the project's root the first time it is executed that captures the exact version of a dependency that the project is using. Subsequent calls to pub get will default to using this file over pubspec.yaml, ensuring the version of the library being used is the correct one for the project. To upgrade a project's dependencies to their latest versions, use the pub upgrade command.

Below is a brief demonstration of the use of packages (as well as more of the language itself). In this example, we construct a simple Dart command-line application to determine whether the provided string is an email address. This takes advantage of the basic_utils package mentioned earlier that includes, among other things, the EmailUtils::isEmail() method:

    import 'package:basic_utils/basic_utils.dart';

    void main(List<String> arguments)
    {
        if (EmailUtils.isEmail(arguments[0])) {
            print("It's an email!");
        } else {
            print("Not an email.");
        }
    }

As shown, we import the basic_utils package using the import statement. The package: prefix of that statement indicates the import is coming from an outside package, rather than from somewhere in the project itself. The statement imports all of the classes defined in the basic_utils package, including the EmailUtils class we use to do the email address validation.

In this example, we have specified an argument to our main() entry point function. This argument is a List (array) of strings (String type) with the name arguments. When this parameter is provided in the declaration of the main() function, Dart will automatically populate it with the command-line arguments given to the application when executed. Since a List is Dart's version of a numerically-indexed array, arguments[0] references the first command-line argument provided (note that in other languages, index 0 is typically the name of the command executed):

    $ dart package-demo.dart someone@example.com
    It's an email!

Wrapping up

There is a lot to like about Dart as a language to write more traditional "applications" in — it is what the language was designed to do. Developers can write applications in any language, and they do, but each language was designed considering a niche it is looking to fill. Dart describes itself on its project page as "a client-optimized language for fast apps on any platform", and specifically as a "language specialized around the needs of user interface creation". Dart stands apart in that sense, as not many languages can claim they were designed with that specific combination of needs in mind.

It is safe to say that we have barely scratched the surface of the capabilities of Dart in this article. Here, we have given a glimpse of the language, with the hope of helping decide if it is worth further examination. The tour of the language and tutorials found in the Dart documentation will be helpful for getting started.



to post comments

A look at Dart

Posted Jul 30, 2020 0:11 UTC (Thu) by koh (subscriber, #101482) [Link] (6 responses)

Sorry to start with a negative comment; however, the article itself seems to rather onesidedly focus on promoting the language without indeed informing the reader about it. Sure, examples are nice and specific domains are nice, but is it bringing any benefit to the current landscape of programming languages? Are there features that make Dart stand out? Byte-code compilation is old, the non-executable "ahead-of-time" binary seems quite useless if there's byte-code / the requirement of a runtime, LLVM can compile everything it has a frontend for to JavaScript, it brings an own package manager (like quite a few languages before)...

What about downsides of Dart? Isn't runtime dependence and (seemingly, since the comparison with C came up quite often) non-strict typedness the combination of the worst features of the languages we currently have? Does it include features not found elsewhere or is it really about its standard library mix providing stuff "around the needs of user interface creation"?

A look at Dart

Posted Jul 30, 2020 9:55 UTC (Thu) by ncm (guest, #165) [Link] (4 responses)

Agreed, it is hard to understand where learning and using this language would benefit anyone.

A look at Dart

Posted Jul 30, 2020 15:24 UTC (Thu) by evgeny (subscriber, #774) [Link] (3 responses)

This would not be a general answer, but in my case, I learned Dart because of Flutter - which is among very few choices if you want to build an opensource app that works both in Android and iOS.

Admittedly, learning (or better say, getting familiarized with) Dart is only a small part of that route...

A look at Dart

Posted Jul 30, 2020 17:55 UTC (Thu) by pjbrooke (subscriber, #8961) [Link] (2 responses)

I've found it a useful language for web and mobile development. I particularly like the strong typing and the compiler messages on error. I'm far more productive in Dart than just writing plain JavaScript.

A look at Dart

Posted Jul 30, 2020 18:20 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

How does it compare with TypeScript?

A look at Dart

Posted Jul 31, 2020 0:31 UTC (Fri) by koh (subscriber, #101482) [Link]

What does strong typing mean? Are the "primitive conversions" still allowed to be implicit, e.g. int <-> double? I read that at least strings need to be .parse()'d.

A look at Dart

Posted Aug 13, 2020 7:46 UTC (Thu) by re:fi.64 (subscriber, #132628) [Link]

Worth noting that Dart's type system is strict and provably sound, with null-safe types in public preview.

Personally, I like it a lot because it has a lot of nice features without being incredibly complex or awkward. It's a bit hard to describe, but I personally see it as a "greater than the sum of its parts" scenario: stuff like having a sound type system, solid stdlib, sane package manager, incremental and reproducible build system with easy source generation, JIT for development, AOT for deployment, a graphical memory observer, ... These are all things other languages can do, but few can do them *all*. In other words, it's relatively unexciting at a glance, but is super easy to pick up and plain works well in practice.

A look at Dart

Posted Jul 30, 2020 13:51 UTC (Thu) by kjp (guest, #39639) [Link] (12 responses)

Well, the article showed me that I'm never going to care about yet another Google NIH language. C#/mono can compile full AOT or partial AOT.

A look at Dart

Posted Aug 17, 2020 10:58 UTC (Mon) by jem (subscriber, #24231) [Link] (11 responses)

Pot calling the kettle black? C# is the mother of all NIH languages.

I have always wondered why C# wasn't an AOT language from the start. MS copied the VM idea from Java, but why? Sun wanted to control the platform, but their Solaris was losing to Windows, so they created a platform on top of the existing operating systems. But Microsoft already controlled the platform (Windows).

A look at Dart

Posted Aug 17, 2020 12:26 UTC (Mon) by farnz (subscriber, #17727) [Link] (10 responses)

Microsoft has a platform problem of its own; Windows is only the foremost platform because it's the one where all your legacy stuff will run. This means it's hard to replace Windows, because you need to handle all that legacy, but it's also hard for Microsoft to evolve the Windows platform, because that runs the risk of breaking legacy stuff.

Remember that the legacy platform has design decisions embedded into it that are now hard to change, dating all the way back to Windows 1.0 atop the 8086/8088 in 1985; they know that some of those decisions are now hurting, but they can't fix the legacy platform without breaking legacy apps, which gives them problems. .NET was one attempt to have a new platform for "modern" code, while letting the legacy work as-is, with the added bonus for Microsoft that it's easier to port .NET apps from x86 to IA64/ARM/other ISA. It looks like they've finally succeeded in separating "legacy" from "modern" with Universal Windows Platform, and are now in a position to evolve UWP ignoring the legacy Windows side, but it's something they've been trying to do for a long, long time.

A look at Dart

Posted Aug 17, 2020 17:25 UTC (Mon) by jem (subscriber, #24231) [Link] (3 responses)

>It looks like they've finally succeeded in separating "legacy" from "modern" with Universal Windows Platform, and are now in a position to evolve UWP ignoring the legacy Windows side, but it's something they've been trying to do for a long, long time.

Or did they succeed? Googling for "UWP dead" gives 1.5 million hits. Apparently it wasn't that popular among developers. Sometimes I get the feeling Microsoft doesn't even care about Windows anymore. Look at YouTube videos from Microsoft's *own* events, where their *own* people are doing presentations using Macs. Even high-profile apps like Microsoft Teams are built using Electron, not .NET.

A look at Dart

Posted Aug 17, 2020 17:49 UTC (Mon) by Cyberax (✭ supporter ✭, #52523) [Link] (2 responses)

UWP is kinda in a zombie state. It was not successful in moving existing applications off the Win32 API and for some unfathomable reason nobody wanted to create new applications targeting UWP.

Microsoft has shifted strategy last year, and they are pushing UWP across multiple platforms (including iOS and Android). But it's too little too late.

A look at Dart

Posted Aug 19, 2020 22:17 UTC (Wed) by zlynx (guest, #2285) [Link] (1 responses)

Windows 7 installs are dying away so resistance to UWP will also.

I prefer UWP to using the old Windows APIs. The problem was not losing support for older Windows versions. And soon no one will care.

A look at Dart

Posted Aug 19, 2020 22:21 UTC (Wed) by Cyberax (✭ supporter ✭, #52523) [Link]

The early UWP required basically full rewrite of applications and was hard to use from C++. It's been changed recently by allowing better interoperation with legacy Win32 code (UWP islands) and by untying UWP from the Windows Store.

But this is a bit too late. I can't fathom NOT using Electron these days instead of UWP. It

A look at Dart

Posted Aug 17, 2020 22:02 UTC (Mon) by Wol (subscriber, #4433) [Link] (5 responses)

> This means it's hard to replace Windows, because you need to handle all that legacy, but it's also hard for Microsoft to evolve the Windows platform, because that runs the risk of breaking legacy stuff.

Didn't IBM solve that problem 50 years ago? Or was MS's attempt at WoW (Windows on Windows) a failure?

As I understand it, IBM CICS runs on OS/360, which runs on OS/370, which runs on ...., which runs on the latest z900 hardware ...

It would be nice if I could run 16-bit Windows on top of 32-bit Windows on top of 64-bit Windows 10, so all my old applications would run. I've got a fair few Win95/8 (and even earlier) programs which it would be nice still to be able to run.

Cheers,
Wol

A look at Dart

Posted Aug 18, 2020 8:36 UTC (Tue) by rschroev (subscriber, #4164) [Link] (1 responses)

> It would be nice if I could run 16-bit Windows on top of 32-bit Windows on top of 64-bit Windows 10, so all my old applications would run. I've got a fair few Win95/8 (and even earlier) programs which it would be nice still to be able to run

I regularly run some old 16-bit software in a 32-bit Windows XP VM (VirtualBox) running on 64-bit Windows. It's not a perfect solution (not as seamless as native support would be, and it requires a copy of Windows XP) , but it works.

A look at Dart

Posted Aug 18, 2020 11:12 UTC (Tue) by Wol (subscriber, #4433) [Link]

I've got a working XP vm, but of course the other problem is that Microsoft has shut down the validation servers, so it's impossible (without cracking it) to get a new working copy of XP.

And iirc a decent chunk of software either doesn't work, or doesn't work properly, on XP. For example I have the Terry Pratchett game (which was notoriously buggy :-( and doesn't even work properly on 98!

At some point I need to get a copy of WP6.1 for Windows working again (that, or WP8 for linux, which requires libc5 ...) - unless of course I get a copy of the latest WordPerfect which I bet has had most of the WPisms replaced with Wordisms and is crap as a result...

My wife has some artist program (converts photos into paintings) which again was buggy on 98/2000 and iirc broke completely on XP ...

Cheers,
Wol

A look at Dart

Posted Aug 18, 2020 8:40 UTC (Tue) by james (subscriber, #1325) [Link] (1 responses)

The rate things are going, we might be able to run 16-bit Windows programs on Wine on Linux inside WSL on 64-bit Windows. And yes, I can imagine that some people might find that useful.

(We can already run 16-bit Windows programs on Wine on x86-64 Linux. It was only Microsoft that took out all 16-bit support on x86-64: the hardware still supports 16-bit protected mode in long mode, just not v86.)

And, of course, there are virtual machines.

A look at Dart

Posted Aug 18, 2020 12:53 UTC (Tue) by nybble41 (subscriber, #55106) [Link]

You joke, but I've actually *done* that. With QEMU user mode emulation thrown into the mix to support 32-bit Linux binaries under WSL on 64-bit Windows. Binfmt support was a bit of a pain since WSL skips the usual startup sequence, but it worked (more or less) once I configured it manually.

A better solution, however, is OTVDM[1], which provides a WineVDM-based alternative to the Virtual DOS Machine (VDM) subsystem which was not implemented natively for 64-bit Windows. (This is still a work in progress; I've had better luck with the development version than the latest formal release.)

[1] http://www.columbia.edu/~em36/otvdm.html

A look at Dart

Posted Aug 18, 2020 9:28 UTC (Tue) by farnz (subscriber, #17727) [Link]

The problem Microsoft has is different to IBM's problem. WoW works just fine for allowing the OS to be updated without requiring legacy apps to be updated; however, people still write apps for the Win32 API, which is full of design decisions from Win16, which in turn has a certain amount of MS-DOS legacy, and of course DOS has CP/M legacy.

IBM has no direct equivalent - people did write code for CPF as well as for MVS, and they moved from MVS to PC-DOS and/or AIX. Microsoft can't get people to do the equivalent of moving MVS to CPF - when they attempt it, people come up with ways to use CPF stuff from MVS (stubs etc) so that you don't need to migrate.

Now, I'm not saying this is a bad problem to have, but it's a problem for Microsoft nonetheless; they cannot persuade developers onto APIs that replace the misdesigns in Win32, because developers don't move from the Win32 platform onto the new platform. Worse, they include escape hatches like P/Invoke, which developers latch onto and use for core functionality.

It's notable that Microsoft was only able to kill off Win16 entirely by refusing to support it on modern hardware (AMD64), despite it having been deprecated for 10 years by that point. And even then, they had to include a set of compat shims that replaced common Win16 code with Win32 equivalents. Had they not done that, there would be new Win16 code being shipped now, 25 years after Win32 was supposed to replace it.

I'm sure it has its niche

Posted Jul 31, 2020 2:13 UTC (Fri) by gus3 (guest, #61103) [Link]

The syntax is generally C-like, but it has Python-inspired things like iterators and scoped exceptions. To my un-informed eyes, it really looks like a mix of the best features of each language.

Why Dart?

Posted Aug 3, 2020 5:29 UTC (Mon) by saadk (guest, #139975) [Link]

While I enjoyed seeing some examples and the overview of some of its features, the article falls short of explaining why readers should look at Dart and what makes it really stand out in the overcrowded programming language landscape. If you use Flutter, Dart is a must. But apart from this, how and why Dart is a better or interesting alternative for other languages?


Copyright © 2020, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds