Tuesday, October 11, 2016

What’s your favorite Dart feature?

Earlier this year we had an ongoing thread where Googlers shared their favorite Dart feature—perhaps a less known one. We wanted to share these insights from some of our most engaged internal users more broadly. Hence this post.

The names have been changed to protect the innocent, but the contents should constitute some fine reading.

Here we go:



What's your favorite "less known" (or undocumented) feature of Dart?
Any particular syntax trick or library function?

Java has loads of these, tricks ranging from the double-paren. init (Map<String,String> {{ put("a", "1"); }};) to "sneaky throwing" ANY exception.
Anything similar in Dart?

Cheers,
Agile Analyzer



As a Java developer, my opinion of both of those is: this is clever, but don't do it in real code under any circumstances.

-Bug Basher



I'm not a fan of tricks, and from what I've seen so far Dart needs very few. There are some features of the language that I feel are underutilized or are very new. Here are a few:

- Null is a type, so if you have a Future and you want to document that it doesn't return anything useful, make it a Future<Null>
- You can throw arbitrary objects in Dart without losing stack traces: throw "This is an error"
- const can be a local variable and it will still be allocated only once (no need to hoist it into a static field or top-level constant)
- You can create custom matchers and use them in your tests (example)
- There exists package:meta that the analyzer understands, and which has @protected, @required (for required named parameters), and others
- You can implement classes as every class also exports its interface.

-Clever Compiler



My favorite lesser-known feature is pretty simple: you can define stuff outside of classes.

I see a lot of users coming to Dart from Java or C# and they are in the habit of stuffing every variable or function inside a class either because they think you have to—which you do in those languages—or out of a belief that it makes their code better in some vague "OOP paradigm is best paradigm" sort of way.

Object-orientation is a fantastic paradigm for expressing some kinds of code. It works really well when you have interesting state and interesting operations that are deeply tied to that state. It's also great when you want polymorphism based on kinds of objects.

But a lot of times, you're just trying to express some vanilla data or some imperative behavior. For those, classes don't provide any value. If you just want to define the value of π somewhere, you don't need a class named, like, I dunno "MathematicalConstants" to put it in. You can just do:

const pi = 3.1; // Eh, close enough.

Likewise, sometimes all you need is a procedure, not a method. Say you create and file and write some string to it. You could make a "FileWriter" class or something, but why bother? You can just do:

void writeFile(String path, String contents) { ... }

You might be thinking that you need to put definitions inside classes to avoid name collisions and to "namespace" things, but you don't need to do that in Dart. Libraries in Dart are the unit of namespacing and modularity. With show, hide, and import prefixes, libraries are much better at expressing namespaces and dealing with name collisions.

If you made it this far, I will note one feature that really is rarely used. Did you know that you can define getters and setters at the top level too? You can define something that looks like a top-level variable but is actually doing computation.

Cheers!
--Dart Dogfood



+1 for simple short-hands.  When a language is young, there's a tendency to use it like that-other-thing-you-know.  These conversations are a really good thing.

Instead of writing:

if (foo == null) {
  foo = 'bar';
}

Just do:  foo ??= 'bar';

The "cognitive load" (ahem!) is much lower.  It's similar to Ruby's foo ||= 'bar' but better, because it strictly applies to nulls (vs. nil & false).

-Eager Execution



I like ??= for lazy-initialized fields

   T _foo;
   T get foo => _foo ??= _computeValue();

--Failed Function



I wouldn't describe these as "favorite" features, but they do seem to be lesser known:


+1 to avoiding things that are "clever" but hard to read.

--Great Googler



I don't see generators used all that much and there are some places where they really help.
https://www.dartlang.org/articles/language/beyond-async#generators

For some use cases a generator is going to be more readable than the alternatives, in others it's very tempting to write a 'clever' generator.

--Heated Hotspot



One of my favourite features is that the analyzer will tell you if you use a switch statement with an enum and don't list all the enum values.

This means that if you've been careful always to use switches when using enums (rather than if/else chains), you can just add the new value to the switch, run the analyzer, and it'll tell you all the places you need to update.

--Injected Iterator



Thanks everybody!!!

Lots of stuff to try out and/or read about! I've been actually thinking that something like the meta library would be nice (and hey, it's already live!)

Awesome thread :)

--Agile Analyzer



My favourite Dart things are foremost the ones I created ;) ... immutable collections, immutable value types, proper enums, and object oriented serialization:

- https://github.com/google/built_value.dart/blob/master/libraries_for_object_oriented_dart.md

And a few I didn't:

 - The ".." operator is a gift for anyone who likes fluent APIs.
 - source_gen (and now the "build" package) really hits the sweet spot for code generation.

Cheers
--JIT Juggler



Someone I was talking to mentioned another of my favourites - using interpolation instead of toString.

    x.toString()

can usually be replaced by

    '$x'  or  '${x}'

It is shorter, looks like it should be a string, and guarantees the result is a String (toString() may perversely return null, even in checked or strong mode).

--Failed Function



Kinda late for the party ;)

I just started to use Dart for two months, still so much to learn. Other than null-aware operators that is already mentioned, I also find double-dot operator very handy for method cascade.

Instead of doing:
o = new MyObject();
o.set(x);
o.set(y);

Now we can:

o = new MyObject()
   ..set(x)
   ..set(y);

--Klever Kompiler



Here's some I like:

You can use 'expand' to concat lists:
[[1,2,3], [4,5,6], [], [7,8,9]].expand((x) => x) // returns [1,2,3,4,5,6,7,8,9]

I don't think this pattern is used often enough, abstract classes which have a forwarding factory constructor to the real implementation:

abstract class Foo {
  factory Foo() = _Foo;
  // ... Foo methods ...
}

class _Foo implements Foo {
  // ... implementation  ...
}

This lets you put all your documentation on 'Foo' and let people call 'new Foo()' while the private class '_Foo' actually has all the implementation details that nobody cares about.

--Linked List



This may not come up that often, but I love the * operator on Strings (thanks Failed Function!):

_padRight(String message, int columns) {
   var padding  = " " * (columns - message.length);
   return '$padding$message';
}

--Main Method



I wouldn't call it a feature, but lot of people don't seem to realize (is it something originating from Java?) you can pass any function/method tearoff as an argument, not necessarily an anonymous literal, ie:

Instead of xs.map((x) => foo(x)) do xs.map(foo).

--Non Nullable



I really like functional methods in combination with tear-offs:

int bestScore = [1, 2, 5, 1].reduce(max);

And generators.

/// Merge [a] and [b] until [a] is fully consumed. Then add 42.
Iterable<int> combine(Iterable<int> a, Iterable<int> b) sync* {
  var aIterator = a.iterator;
  var bIterator = b.iterator;
  while (aIterator.moveNext()) {
    yield aIterator.current;
    if (bIterator.moveNext()) {
      yield bIterator.current;
    }
  }
  yield 42;
}


Seriously, though, this whole thread should be a blogpost on news.dartlang.org.

--Optimized Object



The only question remaining now is:

What is your favorite Dart feature?