public
Last active

Why or why not Dart?

  • Download Gist
why-or-why-not-dart.md
Markdown

Why or why not Dart?

This is my analysis of the Dart language. Overall, some parts I like, some parts I don't like and my overall view of it all.

My background

Depending on what my employers have required on the backend, I've used C#, node.js, PHP, Python & dashes of Ruby. I personally enjoy Javascript, and I'm especially a fan of node.js. I also really like CoffeeScript, and now that it has source maps, it could very well become something I use daily.

Below are my thoughts on Dart:

Modularity

To import a package in Dart (let's say the HTML package), you do this:

import 'dart:html';

This makes me sad because it imports all public identifiers into the global namespace, and you have no idea what they might be. Node.js (specifically CommonJS modules) forces you to assign any imported functionality to a local name.

fs = require('fs');
fs.readFile(file, ...);

You might have to type in a few more characters, but readability is so much better.

However, the following is supported in Dart:

import 'dart:html' as html;

So it's not too bad. Perhaps someone will write "Dart: The Good Parts" and include a linter with that.

Modularity take 2

"Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function." - John Carmack

In node.js, many modules work like this:

module.exports = function(...) {
  ...
    return blah;
}

// in the code that uses the package
var functionName = require('module');

Some might barf at the thought of publishing one function as a package, but hear me out...

  • It limits bloat significantly.
  • It focuses your code at a specific abstraction level.
  • And more importantly, you don't feel stupid publishing a package with one function.

All this makes your code much easier to read (almost fun, dare I say).

In Dart, it's not possible to do this nicely. You do have:

import 'library' show function1;

But this is ugly, plus you cannot rename the function without writing another assignment statement, which then makes your code look dumb because you're polluting your namespace with duplicated functionality.

I hope there will be a way to rename imported functions in the import statement itself.

Modularity anti-pattern

This is an abbreviated example taken from http://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html#libraries-implementing.

library ballgame;

part 'ball.dart';
part 'util.dart';

// ...Code might go here...

The second file, ball.dart implements part of the ballgame library:

part of ballgame;

// ...Code goes here...

The third file, util.dart, implements the rest of the ballgame library:

part of ballgame;

// ...Code goes here...

Enough said...

No actually, let me explain.

IMHO, I'm not sure why this functionality was included, because it limits "findability" in a very big way. I would see this sort of code as a smell.

For example, you're looking for a specific function in a library. But it might be in the one of many text files linked to the library. So you not only have to do a "find" in one file, but you'd have to repeat it in many files. For people who like vim, Sublime or Textmate, sorry but you're royally screwed without plugins.

Now, the Dart guys will turn around and say, "Hey, you can use our free & cool Dart Editor and press F3 to go to the declaration under cursor". But I can tell you that if you can't use a simple text editor, you're making things too complicated and alienating a whole bunch of developers.

Plus if enough newbs think this is an awesome feature and stuff a ton of functions into one library through a hundred part of files (OK slightly exaggerating), we won't have modular libraries, but big ugly libraries that make the world a sadder place.

Now imagine debugging this sort of code in a web browser.

Or better yet, reading it in Bitbucket or Github...

This is a big one for me. I would have to say that reading other people's node.js code in Github has been a really great experience. I would probably include Python in there as well (except for code with import *). Other languages usually have me wondering where a particular function exists, which is a big time waster.

I can see Dart failing at this with its global import, part & part of statements. For a decently sized project, you'd have to clone the source code and use the editor to jump through various files to understand what is going on.

In the spirit of being terse...

I could expand alot more on the points below, but this article would get ridiculously long. Instead, I've provided links and my personal thoughts where necessary:

Making Javascript less weird = awesome!

  • Effective Javascript is a good book, but it's also why Javascript is painful for newbs.
  • Truthy and falsy values are simplified in Dart, which IMHO improves readability.
  • Forgetting the new keyword for constructor functions in Javascript results in unpredictable behaviour. Dart doesn't have that problem.
  • Some may like the prototype system in Javascript, but I think it's probably caused more trouble than it's worth (and yes, I'm very familiar with how it works). Dart goes back to a more traditional OOP paradigm.

Dart wins

  • cascade operator (..)
  • named parameters
  • operator overriding
  • optional typing
  • multi-line strings & string interpolation (CoffeeScript already does this well, but Javascript doesn't yet)
  • Better debugging with Dart Editor

Details for the above are covered in http://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html and other places.

Promises

One area where I feel Dart shines is taking futures/promises & streams seriously. Compared to node.js, I feel that promises read better than callbacks.

Now someone in the node.js community might say, "we have loads of awesome promises libraries". I'd say that's great, but I'm reluctant to use them.

Why? If not implemented natively, promises do impact performance. Dart has it in core, so my guess is that the VM will optimize it to the hilt. But don't quote me... can someone do a benchmark and let me know whether Dart futures are at least equivalent in performance to callbacks? :-)

Performance is a feature, and a damn important one

What's needed

  • Indentation sensitive syntax (I seriously think this is a big win - eliminating all the various types of brackets makes code much faster to type & easier to read)
  • Profiler
  • REPL

Looking ahead

Javascript was forced upon the world because it was the only language available for client-side scripting. It was initially super horrible, but has improved by leaps and bounds over the years. It won by default.

Now Dart has come to compete with Javascript. I really like this. I don't get why Brendan Eich, Douglas Crockford, Microsoft, etc. are hatin' on it. Well, I know why they do, but it only keeps Javascript honest.

But how will Dart win? I can talk all day about the language and its pros and cons, but this is probably how it'll play out. Once Chrome is the dominant browser (already is in many parts of the world), Dart is available in stable Chrome (soon) and important apps like Gmail, etc. use it and demonstrate major performance improvements (especially on mobile devices), many others will jump on board. Plus with a quiet achiever like Lars Bak (who brought the world the Java Hotspot VM and V8), I think their chances are very good indeed over the next few years.

References

Great post!

Re the comment about simple text editor...

One of the design aims of Dart is that it's "toolable" - although you can write Dart with a simple text editor, you're missing out on some of the productivity benefits that you get from using a language designed with tools in mind. After all, you can code Java or .net languages with a simple text editor, but why would you want to?

(Also, WebStorm / IntelliJ Idea has a Dart plugin, if you really don't like the Dart Editor)

hehe I'd prefer the Dart Editor over WebStorm / IntelliJ! I actually think the Dart Editor is pretty good :-).

has improved by leaps and bounds over the years (I'd say mostly due to V8).

That is a very odd comment: V8 has improved the performance target for javascript runtimes — which likely would have improved regardless: Nitro (Squirrelfish Extreme)'s first release follows V8's by 2 weeks and although TraceMonkey would only "go stable" in FF 3.5 a year later, the mozilla team had started working on it in June 2008 and had results to show by April that year, before V8 was announced — but it has made rather little contribution to the language itself, Mozilla has done far more to push the language forward.

Kind of weird that most of the problems that are being identified are actually language features that end up making code smaller and more concise, which are languages features that can be optionally used or not. I get the feeling that most of the disadvantages listed here are just things that are done differently and you are used to having to do in node.js because of limitations in the language, that you've later found and attached some benefits to, rather than ideal language features with merits that stand on their own.

Seems weird that a declarative module/import system can be viewed as a bad thing, node.js require is done the way it is because that's the most concise way it can be done in node.js without an in-built module system.

Creating more boilerplate by exposing a function is easier to read? I'm just not able to follow the logic here.
You can easily identify what dependencies a package has and what it exposes by looking at the header of each library which maintains it in a concise, readable and declarative form - again another area where I can't see how having exports litered throughout the code-base which devs need to scan the whole code-base to find can be considered an advantage.

Being able to split out your source files by using library/part is a blessing, which is similar to using partials in C#. i.e. we're not artificially forced to create new classes and abstractions/delegations because the source code is getting too big, as we can group different parts of a library into cohesive and logical parts, that has no effect to the external surface area of the API.

I find Batteries included for core functionality is a great win, instead of using/finding adhoc libraries to do the same thing, you can instead use the built-in libraries that are always well thought out and written in idiomatic dart that provides a consistent development experience across the entire platform. The libraries also smoothes out browser quirks behind a consistent API. Not having built-in collections, or not having all async APIs return Futures makes composability harder since different libraries will import and implement their own, and using adhoc libraries that don't follow the same conventions, unnecessarily increases institutional knowledge that is a productivity drain and a source of bugs.

Tree-shaking encourages the developer to be lazy and is why it shouldn't be used?

This defies logic, wasting time and being forced to find and use minimal libraries not because of technical fit but because of how much your application uses and how much it adds to the overall download size - has only downsides. This developer time-sink exercise is just an artificial complexity that doesn't add any benefits to the end-user. Knowing that your production code will be tree-shaken and optimally packaged for deployment means you can maintain the most optimal development environment you wish as your code will be in its most optimal form when its deployed, so you're free to reference the most suitable library, use as much or as little of it as you need without concern about resulting code-size, which ends up having a smaller output than having hand-picked micro libraries since only the parts of the library that you end up using gets deployed and not the entire library.

One of the benefits I got when using the Closure Library (i.e. what Gmail, Google Maps were built-on) was the vast array of well-tested and robust libraries that I could use without any concern of its deployment footprint, being able to freely use any library you wish is what Tree-shaking allows.

@masklinn, you're right - I really don't have that much knowledge to comment on that area. I'll remove that reference immediately.

@mythz, not sure whether something was lost in translation, but what I was talking about is reading through code, seeing a function and knowing exactly where that function lives in the code. This is not necessarily easy to do if you're reading code outside the Dart Editor.

i.e. we're not artificially forced to create new classes and abstractions/delegations because the source code is getting too big...

I guess this is where our opinions go beyond the language itself. I'm definitely more a fan of microlibraries, and as a result, the dependency tree in my projects are definitely larger. But I've found that it's much easier to hone in on the parts I need to focus on. You may find otherwise, and that's fine as another school of thought. Like I said, I still work with those frameworks quite often!

This minimalist mindset does extend into my dislike of the "batteries included" philosophy. Dart says it's "batteries included", but its core libraries are actually pretty good. Time will tell as to what will happen there.

I know it may sound counterintuitive to say that "tree shaking" could be a bad thing. Again, this goes back to our difference of opinions (i.e. microlibraries). And some language constructs & tools do encourage technical debt. For example, you'd probably agree when I say multiple inheritance can make things more complicated than they need to be. Now I'm not saying multiple inheritance is like tree shaking, but it can sometimes be used as an easy way out. That's all.

This developer time-sink exercise is just an artificial complexity that doesn't add any benefits to the end-user.

Code is not written for end users, it's written for other programmers.

Again, let me reiterate that if third party code works fine, great! But if it blows up, it's no fun to read through a large codebase to work out what went wrong. I've had to trawl through frameworks like django & jQuery to work out issues. I'd rather invite the pain upfront by choosing a set of small, well-defined microlibraries to do the job. They're also much easier to replace if you encounter something better in the future.

But in saying that, if I was in an agency doing small jobs for multiple clients (time being a major constraint), my approach might change again. Horses for courses!

Regarding the "Batteries Included" approach. Don't confuse this with requiring that you use our batteries.

The libraries you listed: internationalization, mocking, serialization & unit testing are not truly "core" libraries. They are packages like anything else on Pub and you can use something else. But Dart developers need them, and it's relatively important that we have very good benchmark implementations that set the bar high.

There can be competitors and innovation, but they probably won't see much uptake unless they're incredibly good, and I personally think that's a Very Good Thing. A proliferation of mediocre libraries simply because there's a vacuum doesn't do the community any good.

The true core libraries are those included in the SDK. dart:core, dart:async, dart:html, dart:io, dart:collection are the biggies. I think these are exactly the types of things that should be common, and very high quality, across all other higher-level libraries. The rest of the libraries you see on api.dartlang.org without the dart: prefix are included there for convenience.

@justinfagnani, ahh yes - I was wondering what the difference was there. I'm going to have to make some significant revisions now in light of that and have a rethink - thanks!

@justinfagnani, I actually think a proliferation of mediocre libraries does do the community alot of good.

Developers have different preferences regarding coding style & organisation, and node.js' extremely open package management system (npm) has been invaluable to its growth & innovation over the past few years.

BTW, thank you so much for your observation about "dart:" packages. I've substantially revised the article as a result, and I'm going to deep dive into Dart to give it a serious go!

I just stumbled up on this as I was looking to learn about Dart.

I liked your analysis until the end. Even if chrome does manage to get quite a bit of marketshare over what it has today it's a non starter unless FF and IE pick Dart it up as well (and they have stated they never will). It's hard to imagine even google using it for gmail, etc, unless they are really willing to keep two separate codebases as depending on a Dart -> JS compiler seems very risky and the last thing they want to do is make their stuff slower for everyone else.

Dart is interesting and it's performance characteristics are really impressive (not to mention the talent they have behind it) but I'm guessing ecmascript 6 will take the last bit of wind out of it's sails.

@cendrizzi, everything has a beginning - even Javascript had an extremely humble beginning. FF & IE might eat their words one day if Dart gains enough popularity.

Why is it hard to imagine the Gmail team using it? If it was shown that Dart provided a visible performance improvement for Chrome users, I wouldn't find it surprising if at worst, they'd maintain two code bases for it (i.e. Dart & JS).

And plus, dart2js is always a work in progress. You have to realize the guys who work on Dart also work or have worked on V8, and the DeltaBlue benchmark shows it.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.