Gray Soft / Throwing Dartstag:graysoftinc.com,2014-03-20:/categories/232014-10-31T22:16:09ZJames Edward Gray IIHow to Avoid Taking a Dart to the Kneetag:graysoftinc.com,2014-10-31:/posts/1342014-10-31T22:16:09ZAn enumeration of the major mistakes I've made as a Dart newbie.<p>I've been playing with <a href="https://www.dartlang.org/">Dart</a> quite a bit lately. I really enjoy the language, but there are always snags that trip up those coming from other backgrounds. Here are the top three issues that have bit me in Dart, in the hopes of saving others some pain:</p>
<h4>The Truth and Nothing But the Truth… Literally!</h4>
<p>One of the challenges of any language is figuring out what it considers to be truthy in conditional expressions. Each system has its twists, but I find Dart to be extra strict in this case.</p>
<p>Here's some code illustrating the rule:</p>
<div class="highlight highlight-dart"><pre><span class="kt">bool</span> <span class="n">isTruthy</span><span class="p">(</span><span class="kt">Object</span> <span class="n">condition</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">!!</span><span class="n">condition</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="n">tests</span> <span class="o">=</span> <span class="p">[</span><span class="kc">true</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="m">42</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="p">[</span> <span class="p">],</span> <span class="k">new</span> <span class="kt">Object</span><span class="p">()];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">test</span> <span class="k">in</span> <span class="n">tests</span><span class="p">)</span> <span class="p">{</span>
<span class="n">print</span><span class="p">(</span><span class="s2">"</span><span class="si">$</span><span class="n">test</span><span class="s2"> is </span><span class="si">${</span><span class="n">isTruthy</span><span class="p">(</span><span class="n">test</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>That outputs:</p>
<pre><code>$ dart truthiness.dart
true is true
false is false
null is false
42 is false
0 is false
is false
[] is false
Instance of 'Object' is false
</code></pre>
<p>As you can see the literal <code>true</code> (just that <strong>one</strong> object) is truthy in Dart and <strong>everything</strong> else is considered <code>false</code>. I'm in the habit of playing pretty fast and loose with truthiness from all of my time working with Ruby, so this has surprised me a few times.</p>
<p>The best defense, in my opinion, is to be really strict with the return values in places where you are expecting booleans (<code>bool</code> in Dart). I recommend using <strong>only</strong> <code>true</code> and <code>false</code>.</p>
<p>Dart itself has the same recommendation, by the way. If you run the code above in <em>checked</em> mode, Dart errors out on the first non-<code>bool</code> value:</p>
<pre><code>$ dart -c truthiness.dart
true is true
false is false
Unhandled exception:
type 'Null' is not a subtype of type 'bool' of 'boolean expression'.
#0 isTruthy (file:///Users/jeg2/Desktop/truthiness.dart:2:12)
#1 main (file:///Users/jeg2/Desktop/truthiness.dart:8:31)
#2 _startIsolate (dart:isolate-patch/isolate_patch.dart:239)
#3 _startMainIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:192)
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:130)
</code></pre>
<p>There are two things you should notice here:</p>
<ul>
<li>Checked mode, like <a href="http://devblog.avdi.org/2011/06/23/how-ruby-helps-you-fix-your-broken-code/">Ruby's warnings</a>, gives you free debugging help!</li>
<li>Even <code>null</code> failed to qualify as a <code>bool</code> in conditionals. (Don't get too excited yet, until you read the next gotcha…)</li>
</ul><h4>Escaping From Alcatraz and <code>null</code> are Equally Hard</h4>
<p>If you love how <code>null</code> triggered an error in my last example and/or you've spent any time playing Rust's crazy elegant type system, you're almost surely about to be disappointed.</p>
<p>Here's a seemingly harmless chunk of code:</p>
<div class="highlight highlight-dart"><pre><span class="kt">bool</span> <span class="n">liesDamnLiesAndNull</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">print</span><span class="p">(</span><span class="s2">"liesDamnLiesAndNull() returned </span><span class="si">${</span><span class="n">liesDamnLiesAndNull</span><span class="p">()</span><span class="si">}</span><span class="s2">"</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>That prints:</p>
<pre><code>$ dart null_returns.dart
liesDamnLiesAndNull() returned true
</code></pre>
<p>I assume we're fine with this exercise so far. But let's tweak that function a few ways and watch the surprises roll in. Here's the first experiment:</p>
<div class="highlight highlight-dart"><pre><span class="kt">bool</span> <span class="n">liesDamnLiesAndNull</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span> <span class="p">];</span>
<span class="p">}</span>
</pre></div>
<p>Dart kind of allows this:</p>
<pre><code>$ dart null_returns.dart
liesDamnLiesAndNull() returned []
</code></pre>
<p>Don't fret though! "Mind what you have learned. Save you it can." Checked mode to the rescue:</p>
<pre><code>$ dart -c null_returns.dart
Unhandled exception:
type 'List' is not a subtype of type 'bool' of 'function result'.
#0 liesDamnLiesAndNull (file:///Users/jeg2/Desktop/null_returns.dart:2:10)
#1 main (file:///Users/jeg2/Desktop/null_returns.dart:7:62)
#2 _startIsolate (dart:isolate-patch/isolate_patch.dart:239)
#3 _startMainIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:192)
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:130)
</code></pre>
<p>So we're still OK, right? Well, here comes the bad news:</p>
<div class="highlight highlight-dart"><pre><span class="kt">bool</span> <span class="n">liesDamnLiesAndNull</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>That gets a pass, even in checked mode:</p>
<pre><code>$ dart -c null_returns.dart
liesDamnLiesAndNull() returned null
</code></pre>
<p>I guess the logic is that a <code>bool</code> variable can hold a <code>null</code> (makes sense to me as it might not have been initialized), so a <code>bool</code> return value has to mean <code>true</code>, <code>false</code>, or <code>null</code> (makes <strong>no</strong> sense to me). This isn't just about the <code>bool</code> type either. All return values include <code>null</code>.</p>
<p>Now, I hate to kick a reader while they're down, but… well… brace for impact:</p>
<div class="highlight highlight-dart"><pre><span class="kt">bool</span> <span class="n">liesDamnLiesAndNull</span><span class="p">()</span> <span class="p">{</span>
<span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>Again, we're still in checked mode:</p>
<pre><code>$ dart -c null_returns.dart
liesDamnLiesAndNull() returned null
</code></pre>
<p>Ouch. Am I right?</p>
<p>Obviously, the last expression is not automatically returned for you in this language. Worse, if a statement of the form <code>return whatever;</code> is not executed by your code, Dart will add a <code>return null;</code> as the last thing the function does.</p>
<p>This is the biggest item I have disagreed with in the design of Dart so far. I would much prefer it just throw errors as it did for the conditional earlier. It could do so in checked mode, at the very least. <code>null</code> bugs are just too insidious as <a href="http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare">even their creator knows</a>.</p>
<p>OK, we did the bad surprise. It's time for the good surprise.</p>
<h4>Can You Die of Laziness?</h4>
<p>In a word: yes. Here's the code:</p>
<div class="highlight highlight-dart"><pre><span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="n">current_list</span> <span class="o">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="m">100</span><span class="p">,</span> <span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">=></span> <span class="n">i</span><span class="p">);</span>
<span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
<span class="n">current_list</span> <span class="o">=</span> <span class="n">current_list</span><span class="p">.</span><span class="n">where</span><span class="p">((</span><span class="n">_</span><span class="p">)</span> <span class="o">=></span> <span class="kc">true</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>Here's the proof of death:</p>
<pre><code>$ dart -c lazy_iteration.dart
Exhausted heap space, trying to allocate 32 bytes.
Exhausted heap space, trying to allocate 32 bytes.
Exhausted heap space, trying to allocate 32 bytes.
…
</code></pre>
<p>I ran into this issue in a Web page where I had a simple list of items that I was tracking. Each time through the event loop, I would filter out the items I no longer needed to track, using code like you see above. In a browser, this eventually generated stack overflow errors.</p>
<p>The good news is that it's:</p>
<ul>
<li>All my fault</li>
<li>Easy to fix</li>
<li>And for a good cause, in my opinion</li>
</ul><p>Again I'm use to Ruby where iterators are immediate, unless you explicitly make them lazy. In Dart it's often the opposite. Many iterators, like <code>where()</code> above, are just lazy by default. (Rust works this way too.)</p>
<p>I'm a big fan of the lazy-by-default behavior, so don't take this point as a complaint. You just need to remember to force the iterator to be evaluated when needed, which is easy to do:</p>
<div class="highlight highlight-dart"><pre><span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="n">current_list</span> <span class="o">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="m">100</span><span class="p">,</span> <span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">=></span> <span class="n">i</span><span class="p">);</span>
<span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
<span class="n">current_list</span> <span class="o">=</span> <span class="n">current_list</span><span class="p">.</span><span class="n">where</span><span class="p">((</span><span class="n">_</span><span class="p">)</span> <span class="o">=></span> <span class="kc">true</span><span class="p">).</span><span class="n">toList</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>That code can run forever.</p>
<h4>It's Not All Bad</h4>
<p>I'm kind of sad that my first post about Dart ended up being kind of a downer. I'm having fun playing with the language. It's good stuff, if you ask me. You'll just have to wait for the followup articles where I show off the good parts…</p>James Edward Gray II