<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;C0YGRXc-cCp7ImA9WhVUEE4.&quot;"><id>tag:blogger.com,1999:blog-5051684</id><updated>2012-05-14T14:32:04.958-07:00</updated><title>Xenomachina</title><subtitle type="html">Laurence Gonsalves's home page</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.xenomachina.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.xenomachina.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>60</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/xenomachina" /><feedburner:info uri="xenomachina" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>37.40679</geo:lat><geo:long>-122.074613</geo:long><logo>http://xenomachina.com/xm-feed-logo.png</logo><feedburner:browserFriendly>This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site, subject to copyright and fair use.</feedburner:browserFriendly><entry gd:etag="W/&quot;AkYGSH4_eyp7ImA9WhRbGUw.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-3690557693431902083</id><published>2012-02-10T14:42:00.001-08:00</published><updated>2012-02-10T15:55:29.043-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-10T15:55:29.043-08:00</app:edited><title>Multi-diff with Vim and Git</title><content type="html">&lt;p&gt;I just pushed some stuff to &lt;a href="https://github.com/xenomachina/public/"&gt;github&lt;/a&gt; that you may find useful if you're either a git user, a vim user, or (best of all) both.

&lt;h4&gt;git-multidiff&lt;/h4&gt;

&lt;p&gt;For git users, there's &lt;code&gt;git-multidiff&lt;/code&gt;, which works kind of like &lt;code&gt;git difftool&lt;/code&gt;, except that it invokes your tool of choice once on the entire set of files, instead of once for each pair. This is handy if you have a diff tool that'll let you view multiple diffs simultaneously.

&lt;p&gt;Full installation instructions are in a comment at the top of the file, but it basically consists of putting &lt;a href="https://raw.github.com/xenomachina/public/master/bin/git-multidiff"&gt;&lt;code&gt;git-multidiff&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://raw.github.com/xenomachina/public/master/bin/_git-multidiff-helper"&gt;&lt;code&gt;_git-multidiff-helper&lt;/code&gt;&lt;/a&gt; in your path and adding an entry to your &lt;code&gt;.gitconfig&lt;/code&gt;. Note that it requires Python (I've tested it with 2.7.2).

&lt;h4&gt;tab-multi-diff.vim&lt;/h4&gt;

&lt;p&gt;Speaking of “diff tools that'll let you view multiple diffs simultaneously”, that's what &lt;code&gt;tab-multi-diff.vim&lt;/code&gt; is for. It lets you do a “vimdiff” on multiple pairs of files, with each pair in a separate tab.

&lt;p&gt;To install it, just save &lt;a href="https://raw.github.com/xenomachina/public/master/vim/plugin/tab-multi-diff.vim"&gt;&lt;code&gt;tab-multi-diff.vim&lt;/code&gt;&lt;/a&gt; in your vim plugins directory (typically &lt;code&gt;~/.vim/plugin/&lt;/code&gt;).

&lt;p&gt;To use it, you can invoke vim (or gvim) with a command like:

&lt;pre class=code&gt;
gvim -c 'silent call TabMultiDiff()' old-foo foo old-bar bar
&lt;/pre&gt;

&lt;p&gt;Thats obviously kind of long, so you probably want to wrap it in a shell script. My script for doing this is &lt;a href="https://raw.github.com/xenomachina/public/master/bin/vd"&gt;&lt;code&gt;vd&lt;/code&gt;&lt;/a&gt; (which also depends on &lt;a href="https://raw.github.com/xenomachina/public/master/bin/v"&gt;&lt;code&gt;v&lt;/code&gt;&lt;/a&gt;).  Note that that it imposes some of my personal preferences, so you may only want to use it as a starting point.

&lt;h4&gt;Using Them Together&lt;/h4&gt;

&lt;p&gt;To use &lt;code&gt;git-multidiff&lt;/code&gt; and &lt;code&gt;tab-multi-diff.vim&lt;/code&gt; together I have the following in my &lt;code&gt;.gitconfig&lt;/code&gt;:

&lt;pre class=code&gt;
[multidiff]
  tool = vd -f
&lt;/pre&gt;

&lt;p&gt;Note that the &lt;code&gt;tool&lt;/code&gt; option for &lt;code&gt;multidiff&lt;/code&gt; is a command line prefix, not a “tool name” as it is for &lt;code&gt;git difftool&lt;/code&gt;. That’s why it’s possible to include a flag. The &lt;code&gt;-f&lt;/code&gt; flag shown here is to prevent backgrounding. (It's always seemed weird to me that &lt;code&gt;git difftool&lt;/code&gt; has this extra layer of indirection.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-3690557693431902083?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/YnRI5rq17xM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/3690557693431902083/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=3690557693431902083" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/3690557693431902083?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/3690557693431902083?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/YnRI5rq17xM/multi-diff-with-vim-andor-git.html" title="Multi-diff with Vim and Git" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.xenomachina.com/2012/02/multi-diff-with-vim-andor-git.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEMFQns9eCp7ImA9WhRQEU0.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-6100989572468162846</id><published>2011-12-04T15:12:00.000-08:00</published><updated>2011-12-05T09:33:33.560-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-05T09:33:33.560-08:00</app:edited><title>A Sufficiently Advanced Violin</title><content type="html">&lt;p&gt;The reactions to the recent story about &lt;a
href="http://www.rsna.org/Media/rsna/RSNA11_newsrelease_target.cfm?id=562"&gt;CT
scans being used to recreate a Stradivarius violin&lt;/a&gt; are interesting.
For example, in the comments on &lt;a
href="http://www.engadget.com/2011/11/30/stradivarius-violin-recreated-from-cat-scan-sounds-amazingly-s/"&gt;Engadget&lt;/a&gt; there's
a lot of denial that it could sound as good as the original, as well as
people saying it won't sound as good in 300 years. I have to wonder if
the latter even matters. If we can cheaply create a clone of a
307-year-old Stradivarius, you can just make a new one when it stops
sounding good. And who knows if a 600-year-old Stradivarius will
actually sound good?

&lt;p&gt;&lt;img border=0 alt="Photograph of CNC machine carving the front plate of the Betts violin reproduction." src="http://xenomachina.com/images/cnc-violin.jpg"&gt;


&lt;p&gt;&lt;a
href="http://www.musicianscentre.com/blog/2011/12/napster-for-music-shops/"&gt;Musicians
Centre has an interesting take&lt;/a&gt;: “Why do we have to keep going back
and trying to replicate the past when it comes to instruments?”

&lt;p&gt;I agree, but I don't think the scanned Stradivarius has to be just about &lt;em&gt;replicating&lt;/em&gt;
the past. If we are able to scan instruments that sound good and produce
replicas, that means we can experiment with modifications to the design,
and iterate to produce better instruments. Without having any way to
measure what makes a Stradivarius “good” means iteration is hard, and
you end up with people talking about trees that don’t exist anymore or a
mysterious fungus that can’t be replicated.

&lt;p&gt;That said, improvements will most likely have to overcome a
subjectivity problem. On a large scale there are objective ways of
determining that one violin is better than another, but at a finer scale
things might not be so clear cut. Assuming you could make a violin that
sounds slightly better than even the best Stradivarius by some objective
measure, would it just end up sounding weird to people who are used to
the real thing?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-6100989572468162846?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/EQHgq7Vr-Zo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/6100989572468162846/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=6100989572468162846" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/6100989572468162846?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/6100989572468162846?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/EQHgq7Vr-Zo/sufficiently-advanced-violin.html" title="A Sufficiently Advanced Violin" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.xenomachina.com/2011/12/sufficiently-advanced-violin.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYEQXs-eCp7ImA9WhZVF04.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-3428627354335756101</id><published>2011-05-29T22:59:00.000-07:00</published><updated>2011-05-29T23:15:00.550-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-29T23:15:00.550-07:00</app:edited><title>PSA: Netflix for Android spontaneous deactivation fix</title><content type="html">&lt;p&gt;Today Netflix on my Android phone (a Nexus One) started giving me this error:

&lt;blockquote&gt;It looks like Netflix has been deactivated on this device. It could be an issue with your account or perhaps your device was deactivated on netflix.com. (2004)&lt;/blockquote&gt;

&lt;p&gt;Netflix only lets you have 6 devices activated per account, so at first I thought I might be bumping into the limit, but it turned out that that wasn't my problem.

&lt;p&gt;The thing that eventually worked was to clear all data for the Netflix app. To do this:

&lt;ol&gt;
&lt;li&gt;Go to the home screen.
&lt;li&gt;Press the menu button.
&lt;li&gt;Select "Manage apps" (or "Settings", then "Applications", then "Manage applications" on older versions of Android).
&lt;li&gt;Select the "Downloaded" tab.
&lt;li&gt;Select the Netflix app.
&lt;li&gt;Click on "Clear data".
&lt;/ol&gt;

&lt;p&gt;The next time you open the Netflix app you'll need to sign in again, but then it should be working correctly.

&lt;p&gt;I talked to Netflix customer support about this issue and apparently they had a ton of devices spontaneously deactivate in the last day or so. It sounded like they either don't really understand the cause, or just didn't want to share the details. Based on the fix it seems like some sort of authentication token either got corrupted or had the server-side rug pulled out from under it. Clearing the app data seems to force it to get a fresh token.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-3428627354335756101?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/Ob7YjxGSZAQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/3428627354335756101/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=3428627354335756101" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/3428627354335756101?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/3428627354335756101?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/Ob7YjxGSZAQ/psa-netflix-for-android-spontaneous.html" title="PSA: Netflix for Android spontaneous deactivation fix" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://www.xenomachina.com/2011/05/psa-netflix-for-android-spontaneous.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUcCRH8_cCp7ImA9WhRXFkU.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-6888811589008147369</id><published>2011-05-09T14:30:00.000-07:00</published><updated>2011-12-23T15:31:05.148-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-23T15:31:05.148-08:00</app:edited><title>Android's 2D Canvas Rendering Pipeline</title><content type="html">&lt;p&gt;This is a conceptual overview of how Android's 2D Canvas
  rendering pipeline works. Since Android's Canvas API is mostly a
  pretty thin veneer on top of Skia it should also serve as a
  reasonable overview of Skia's operation, though I've only looked
  at Skia code that's reachable from Android's SDK, and when the Skia
  and Android terminology differ (which is rare, modulo
  “&lt;code&gt;Sk&lt;/code&gt;” prefixes and capitalization) I've used the
  Android terminology.&lt;/p&gt;

  &lt;h5&gt;How and Why I Wrote This&lt;/h5&gt;

  &lt;p&gt;I wrote this overview because I've been doing some Android
  development recently, and I was getting frustrated by the fact
  that the documentation for &lt;code&gt;android.graphics&lt;/code&gt;,
  particularly when it comes to all of the things that can be set in a
  &lt;code&gt;Paint&lt;/code&gt; object, is extremely sparse. I Googled, and
  &lt;a href="http://stackoverflow.com/questions/5762727/how-do-the-pieces-of-androids-2d-canvas-drawing-pipeline-fit-together"&gt;
  I asked a question on Stack Overflow&lt;/a&gt; but I couldn't find
  anything that explained this stuff to my satisfaction.&lt;/p&gt;

  &lt;p&gt;This overview is based on reading what little documentation
  exists (often “between the lines”), doing lots of experiments to
  see how fringe cases work, poring over the code, and doing even
  more experiments to verify that I was reading the code correctly.
  I started writing it as notes for myself, but I figured others
  might benefit as well so I decided to post it here.&lt;/p&gt;

  &lt;h5&gt;Caveats&lt;/h5&gt;

  &lt;p&gt;I say this is a “conceptual” overview because it does not
  always explain the actual implementation. The implementation is
  riddled with special cases that attempt to avoid doing work that
  isn't necessary. (I remember hearing some quote along the lines
  of “the fastest way to do something is to not do it at all”.)
  Understanding the implementation details of all of these special
  cases is unnecessary to understanding the actual end-result, so
  I've focused on the most general path through the pipeline. I
  actually avoided looking at the details of a lot of the
  special-case code, so if this code contains behavioral
  inconsistencies I won't have seen them.&lt;/p&gt;

  &lt;p&gt;Also, there are cases, particularly in the Shading and
  Transfer sections, where the algorithm described here is far less
  efficient but easier to visualize (and, I hope, understand) than
  the actual implementation. For example, I describe Shading as a
  separate phase that produces an image containing the source
  colors and Transfer as a phase producing an image with
  intermediate colors. In reality these two “phases” are
  interleaved such that only a small set (often just one) of the
  pixels from each of these virtual images actually “exists” at any
  instant in time. There is also short-circuiting in this code
  such that the source and intermediate colors aren't computed at
  all for pixels where the mask is fully transparent (&lt;code&gt;0x00&lt;/code&gt;).&lt;/p&gt;

  &lt;p&gt;This does mean that this overview can't give one an entirely
  accurate understanding of the performance (speed and/or memory)
  of various operations in the pipeline. For that it would be better
  to performing experiments and profile.&lt;/p&gt;

  &lt;p&gt;Also keep in mind that because this is documenting what is
  arguably “undocumented behavior” it's hard to say how much of
  what is described here is stuff that's guaranteed versus
  implementation detail, or even outright bugs. I've used some
  judgement in determining where to put the boundaries between
  phases (all of that optimization blurs the lines) based on what I
  think is a “reasonable API” and I've also tried to point out when
  I think a particular behavior I've discovered looks more like a
  bug than a feature to rely on.&lt;/p&gt;

  &lt;p&gt;There are still a number of cases where I'd like to do some
  more experimentation to verify that my reading of the code is
  correct and I've tried to indicate those below.&lt;/p&gt;
  &lt;hr&gt;
  &lt;h5&gt;Entering the Pipeline&lt;/h5&gt;

  &lt;p&gt;The pipeline is invoked each time a
  &lt;code&gt;Canvas.draw&lt;var&gt;Something&lt;/var&gt;&lt;/code&gt; method that takes a
  &lt;code&gt;Paint&lt;/code&gt; object is called.&lt;/p&gt;

  &lt;p&gt;Most of these drawing operations start at the first phase,
  Path Generation. There are two exceptions, however:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;
      &lt;p&gt;&lt;code&gt;drawPaint&lt;/code&gt; skips Path Generation entirely and
      Rasterization consists of producing a solid opaque mask.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;&lt;code&gt;drawBitmap&lt;/code&gt; has different behavior depending
      on the supplied &lt;code&gt;Bitmap&lt;/code&gt;'s configuration.&lt;/p&gt;

      &lt;p&gt;In the case of an &lt;code&gt;ALPHA_8&lt;/code&gt; &lt;code&gt;Bitmap&lt;/code&gt;,
      Path Generation and Rasterization are both skipped and the
      supplied &lt;code&gt;Bitmap&lt;/code&gt; is used as the mask.&lt;/p&gt;

      &lt;p&gt;For other &lt;code&gt;Bitmap&lt;/code&gt; configurations the
      &lt;code&gt;Shader&lt;/code&gt; is temporarily replaced with a
      &lt;code&gt;BitmapShader&lt;/code&gt; in &lt;code&gt;CLAMP&lt;/code&gt; mode. This means
      that setting a &lt;code&gt;Shader&lt;/code&gt; to be used with a
      &lt;code&gt;drawBitmap&lt;/code&gt; call with a non-&lt;code&gt;ALPHA_8&lt;/code&gt;
      &lt;code&gt;Bitmap&lt;/code&gt; is pointless. The pipeline is then executed
      as though &lt;code&gt;drawRect&lt;/code&gt; had been called with a rectangle
      equal to the bounding box of the &lt;code&gt;Bitmap&lt;/code&gt;.&lt;/p&gt;

      &lt;p&gt;According to Romain Guy, this behavior is intentional.&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;


  &lt;h5&gt;Overall Structure&lt;/h5&gt;

  &lt;div class=figure&gt;
    &lt;object data="http://www.xenomachina.com/android-canvas-pipeline.svg.gz" type="image/svg+xml" width=583 height=1218 class="img" onload="this.getSVGDocument().getElementById('side-label').setAttribute('style', 'display:none')"&gt;
      &lt;img src="http://www.xenomachina.com/android-canvas-pipeline.png"&gt;
    &lt;/object&gt;
    &lt;p&gt;The overall structure of the pipeline.  This diagram is available
    in &lt;a href="http://www.xenomachina.com/android-canvas-pipeline.svg.gz"&gt;Gzipped SVG&lt;/a&gt; or &lt;a
    href="http://www.xenomachina.com/android-canvas-pipeline.pdf"&gt;PDF&lt;/a&gt; formats for use as a
    quick reference card.
  &lt;/div&gt;

  &lt;p&gt;At the top of the diagram are the two main inputs to the pipeline:
  the parameters to the draw method that was called (really multiple
  inputs) and the “destination” image &amp;mdash; the &lt;code&gt;Bitmap&lt;/code&gt;
  connected to the &lt;code&gt;Canvas&lt;/code&gt;.

  &lt;p&gt;There are four main phases in the pipeline. The details of these
  will be covered below. While there are exceptions, all of the phases
  (mostly) follow this pattern: There are two or more sub-phases, the first of
  which computes an intermediate result, while the later ones “massage”
  this intermediate result. These later sub-phases often default to
  the identity function. ie: they usually leave the intermediate result
  alone unless explicitly told to do otherwise by setting properties on
  the &lt;code&gt;Paint&lt;/code&gt;.

  &lt;h5&gt;Path Generation&lt;/h5&gt;

  &lt;p&gt;The output of the first phase is a &lt;code&gt;Path&lt;/code&gt;.&lt;/p&gt;

  &lt;p&gt;This phase has three sub-phases:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;
      &lt;p&gt;An initial &lt;code&gt;Path&lt;/code&gt; is constructed based on the
      &lt;code&gt;draw*&lt;/code&gt; method that was called. In the case of
      &lt;code&gt;drawPath&lt;/code&gt;, this is simply the &lt;code&gt;Path&lt;/code&gt;
      supplied by the client. In the case of &lt;code&gt;drawOval&lt;/code&gt;
      or &lt;code&gt;drawRect&lt;/code&gt;, the output is a &lt;code&gt;Path&lt;/code&gt;
      containing the corresponding primitive.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;If the &lt;code&gt;Paint&lt;/code&gt; has a &lt;code&gt;PathEffect&lt;/code&gt;,
      it is used to produce a new path based on the inital
      &lt;code&gt;Path&lt;/code&gt;. The &lt;code&gt;PathEffect&lt;/code&gt; is essentially
      a function that takes a &lt;code&gt;Path&lt;/code&gt; as its input and
      returns a &lt;code&gt;Path&lt;/code&gt;.

      &lt;p&gt;If no &lt;code&gt;PathEffect&lt;/code&gt; is set then the initial
      &lt;code&gt;Path&lt;/code&gt; is passed on to the next phase unmodified. That
      is, the default &lt;code&gt;PathEffect&lt;/code&gt; is the identity
      function.&lt;/p&gt;

      &lt;p&gt;&lt;code&gt;PathEffect&lt;/code&gt; implementations include
      &lt;code&gt;CornerPathEffect&lt;/code&gt;, which rounds the corners of the
      &lt;code&gt;Path&lt;/code&gt;, and &lt;code&gt;DashPathEffect&lt;/code&gt; which converts
      the &lt;code&gt;Path&lt;/code&gt; into a series of “dashes”.&lt;/p&gt;

      &lt;p&gt;One interesting quirk: if the &lt;code&gt;Paint&lt;/code&gt; object's style
      is &lt;code&gt;FILL_AND_STROKE&lt;/code&gt; the &lt;code&gt;PathEffect&lt;/code&gt; is
      “lied to” and told that it's &lt;code&gt;FILL&lt;/code&gt;. This matters
      because &lt;code&gt;PathEffect&lt;/code&gt; implementations may alter their
      behavior depending on settings in the &lt;code&gt;Paint&lt;/code&gt;. For
      example, &lt;code&gt;DashPathEffect&lt;/code&gt; won't do anything if it is
      told the style is &lt;code&gt;FILL&lt;/code&gt;.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      The final sub-phase is “stroking”. If the &lt;code&gt;Paint.Style&lt;/code&gt;
      is &lt;code&gt;Path&lt;/code&gt; this does nothing to the &lt;code&gt;Path&lt;/code&gt;.
      If the style is &lt;code&gt;STROKE&lt;/code&gt; then a new “stroked”
      &lt;code&gt;Path&lt;/code&gt; is generated. This stroked &lt;code&gt;Path&lt;/code&gt; is
      a &lt;code&gt;Path&lt;/code&gt; that encloses the boundary of the input
      &lt;code&gt;Path&lt;/code&gt;, respecting the various stroke properties of the
      &lt;code&gt;Paint&lt;/code&gt; (&lt;code&gt;strokeCap&lt;/code&gt;,
      &lt;code&gt;strokeJoin&lt;/code&gt;, &lt;code&gt;strokeMiter&lt;/code&gt;,
      &lt;code&gt;strokeWidth&lt;/code&gt;). The idea is that later phases of the pipeline will
      always fill the Path they are given, and so the stroking process
      converts Paths into their filled equivalents. If the style is
      &lt;code&gt;FILL_AND_STROKE&lt;/code&gt; the result &lt;code&gt;Path&lt;/code&gt; is the stroked
      &lt;code&gt;Path&lt;/code&gt; concatenated to the original &lt;code&gt;Path&lt;/code&gt;.
    &lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;The method &lt;code&gt;Paint.getFillPath()&lt;/code&gt; can be used to run
  the later sub-phases of this phase on a &lt;code&gt;Path&lt;/code&gt; object. As
  far as I can tell this is the only significant part of the pipeline
  that can be run in isolation.&lt;/p&gt;

  &lt;!-- TODO: Paint attrs used here --&gt;

  &lt;h5&gt;Rasterization&lt;/h5&gt;

  &lt;p&gt;Rasterization is the process of determining the set of pixels that
  will be drawn to. This is accomplished by generating a “mask”, which
  is a alpha-channel image. Opaque (&lt;code&gt;0xFF&lt;/code&gt;) pixels on this
  mask indicate areas we want to draw to at “full strength”, transparent
  (&lt;code&gt;0x00&lt;/code&gt;) areas are areas we don't want to draw to at all,
  and partially transparent areas will be drawn to at
  “partial strength”. This is explained more at the end of the final
  phase. (When visualizing this process I find that it helps to think of
  opaque as white and transparent as black.)

  &lt;/p&gt;

  &lt;p&gt;Rasterization has two completely different behaviors depending
  on whether a &lt;code&gt;Rasterizer&lt;/code&gt; has been set on the
  &lt;code&gt;Paint&lt;/code&gt;.&lt;/p&gt;

  &lt;p&gt;If no &lt;code&gt;Rasterizer&lt;/code&gt; has been set then the default
  rasterization process is used:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;The &lt;code&gt;Path&lt;/code&gt; is scan-converted based on parameters
    from the &lt;code&gt;Paint&lt;/code&gt; (eg: the &lt;code&gt;style&lt;/code&gt;
    property) and the &lt;code&gt;Path&lt;/code&gt; (eg: the
    &lt;code&gt;fillType&lt;/code&gt; property) to produce an initial
    mask.

  &lt;p&gt;Pixels “inside” the &lt;code&gt;Path&lt;/code&gt; will become opaque, those “
  outside” will be left transparent, and those on the boundary may
  become partially transparent (for anti-aliasing). The mask will end up
  containing an opaque silhouette of the object.&lt;/p&gt;

  &lt;p&gt;The &lt;code&gt;Path&lt;/code&gt; object's &lt;code&gt;fillType&lt;/code&gt;
  determines the rule used to determine which pixels are inside
  versus outside. See &lt;a href="http://en.wikipedia.org/wiki/Nonzero-rule"&gt;Wikipedia's article
  on the non-zero winding rule&lt;/a&gt; for a good explanation if these
  different rules.&lt;/p&gt;
  &lt;/li&gt;

    &lt;li&gt;If there is a &lt;code&gt;MaskFilter&lt;/code&gt; set, then the initial
    mask is transformed by the &lt;code&gt;MaskFilter&lt;/code&gt;. The
    &lt;code&gt;MaskFilter&lt;/code&gt; is essentially a function that takes a
    mask (an &lt;code&gt;ALPHA_8&lt;/code&gt; &lt;code&gt;Bitmap&lt;/code&gt;) as input and
    returns a mask as output. For example, a
    &lt;code&gt;BlurMaskFilter&lt;/code&gt; will blur the mask image.

  &lt;p&gt;If no &lt;code&gt;MaskFilter&lt;/code&gt; is set then the initial mask is
  passed on to the next phase unmodified. That is, the default
  &lt;code&gt;MaskFilter&lt;/code&gt; is the identity function.&lt;/p&gt;&lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;If a &lt;code&gt;Rasterizer&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; set on the
  &lt;code&gt;Paint&lt;/code&gt; then, instead of the above two steps, the
  &lt;code&gt;Rasterizer&lt;/code&gt; creates the mask from the
  &lt;code&gt;Path&lt;/code&gt;. The &lt;code&gt;MaskFilter&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt;
  invoked after the &lt;code&gt;Rasterizer&lt;/code&gt;. (This seems like a
  bug, but I've verified this behavior experimentally. Romain Guy agreed
  that this is probably a bug.)&lt;/p&gt;

  &lt;p&gt;The only &lt;code&gt;Rasterizer&lt;/code&gt; implementation in Android is
  &lt;code&gt;LayerRasterizer&lt;/code&gt;. &lt;code&gt;LayerRasterizer&lt;/code&gt; makes it
  possible
  to create multiple “layers”, each with its own
  &lt;code&gt;Paint&lt;/code&gt; and offset (translation). This means that when
  &lt;var&gt;n&lt;/var&gt; &lt;code&gt;LayerRasterizer&lt;/code&gt; layers are present
  there are &lt;span class=math&gt;&lt;var&gt;n&lt;/var&gt; + 1&lt;/span&gt; &lt;code&gt;Paint&lt;/code&gt; objects in use: the
  “top-level” &lt;code&gt;Paint&lt;/code&gt; (passed to the draw* method) and
  an additional &lt;var&gt;n&lt;/var&gt; &lt;code&gt;Paint&lt;/code&gt; objects, one for
  each Layer.&lt;/p&gt;

  &lt;p&gt;&lt;code&gt;LayerRasterizer&lt;/code&gt; takes the &lt;code&gt;Path&lt;/code&gt; and
  for each layer runs the &lt;code&gt;Path&lt;/code&gt; through the pipeline of
  that layer's &lt;code&gt;Paint&lt;/code&gt; starting at the
  &lt;code&gt;PathEffects&lt;/code&gt; step and rendering to the mask. This has
  some interesting consequences:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;Each layer can have its own &lt;code&gt;PathEffect&lt;/code&gt;. These
      are applied to the &lt;code&gt;Path&lt;/code&gt; that was generated by
      the top-level &lt;code&gt;PathEffect&lt;/code&gt; (if one was set). So if
      the &lt;code&gt;PathEffect&lt;/code&gt; of the top-level's
      &lt;code&gt;Paint&lt;/code&gt; is set to a &lt;code&gt;CornerPathEffect&lt;/code&gt; and
      a layer's &lt;code&gt;PathEffect&lt;/code&gt; set to
      &lt;code&gt;DashPathEffect&lt;/code&gt; that layer will render a dashed
      shape with rounded corners.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;Each layer can have its own &lt;code&gt;Rasterizer&lt;/code&gt;.
      Recursive rasterization is recursive.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;Each layer can have its own &lt;code&gt;MaskFilter&lt;/code&gt;. This
      &lt;code&gt;MaskFilter&lt;/code&gt; applies to a separate mask in the
      sub-pipeline. Remember, the entire pipeline is being run
      again. For example, if there are two layers and one has a
      &lt;code&gt;BlurMaskFilter&lt;/code&gt; the output of the other layer
      will &lt;em&gt;not&lt;/em&gt; be blurred regardless of the order of the
      layers.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The destination &lt;code&gt;Bitmap&lt;/code&gt; of this sub-pipeline
      is an alpha bitmap, so only the alpha-channel component of
      the Shading and Transfer phases have any relevance.&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;Also note that &lt;code&gt;LayerRasterizer&lt;/code&gt; does not make use of
  the &lt;code&gt;MaskFilter&lt;/code&gt; in the top-level &lt;code&gt;Paint&lt;/code&gt;. Since
  the top-level &lt;code&gt;MaskFilter&lt;/code&gt; is not invoked after invoking
  the &lt;code&gt;Rasterizer&lt;/code&gt;, there is no point in setting a
  &lt;code&gt;MaskFilter&lt;/code&gt; on a &lt;code&gt;Paint&lt;/code&gt; if the
  &lt;code&gt;Rasterizer&lt;/code&gt; has been set to a
  &lt;code&gt;LayerRasterizer&lt;/code&gt;. (Perhaps other &lt;code&gt;Rasterizer&lt;/code&gt;
  implementations could make use of the top-level
  &lt;code&gt;MaskFilter&lt;/code&gt;, but &lt;code&gt;LayerRasterizer&lt;/code&gt; is the only
  implementation included with Android.)&lt;/p&gt;

  &lt;!-- TODO: Paint attrs used here --&gt;

  &lt;h5&gt;Shading&lt;/h5&gt;

  &lt;p&gt;Shading is the process of determining the “source colors” for
  each pixel. A color (can) consist of alpha, red, green, and blue
  components (ARGB for short) each of which ranges
  from 0 to 1. (In reality these are typically represented as bytes from
  &lt;code&gt;0x00&lt;/code&gt; to &lt;code&gt;0xFF&lt;/code&gt;.)&lt;/p&gt;

  &lt;p&gt;At a high level, the output of the &lt;code&gt;Shader&lt;/code&gt; can be
  thought of as a virtual image containing the source colors: the “source” image.
  The actual implementation doesn't use a &lt;code&gt;Bitmap&lt;/code&gt;, but rather
  uses a function that maps from
  &lt;code&gt;(x,y)&lt;/code&gt; to an ARGB color (the “source color”) for
  the given pixel, and this function is only called for coordinates
  where the corresponding pixal may be altered by the source color. This
  is really just an optimization, however.&lt;/p&gt;

  &lt;p&gt;Like the previous phases, Shading also has two sub-phases:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;
      &lt;p&gt;An initial “source” image is generated by the
      &lt;code&gt;Shader&lt;/code&gt;. If no &lt;code&gt;Shader&lt;/code&gt; has been set
      it's as if a &lt;code&gt;Shader&lt;/code&gt; that produced a single solid
      color (the Paint's Color) was used.&lt;/p&gt;

      &lt;p&gt;The &lt;code&gt;Shader&lt;/code&gt; does not get the mask, the
      &lt;code&gt;Path&lt;/code&gt;, or the destination image as inputs. &lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;If a &lt;code&gt;ColorFilter&lt;/code&gt; has been set then the colors
      in the source color image are transformed by this
      &lt;code&gt;ColorFilter&lt;/code&gt;.&lt;/p&gt;

      &lt;p&gt;The only input to the &lt;code&gt;ColorFilter&lt;/code&gt; during the
      pipeline are ARGB colors. The &lt;code&gt;ColorFilter&lt;/code&gt; does not
      get the mask, the &lt;code&gt;Path&lt;/code&gt;, the destination image, or the
      coordinates of the pixel whose color it is transforming, as inputs.&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;

  &lt;!-- TODO: Paint attrs used here --&gt;

  &lt;h5&gt;Transfer&lt;/h5&gt;

  &lt;p&gt;Transfer is the process of actually transferring color to the
  destination &lt;code&gt;Bitmap&lt;/code&gt;. The transfer phase has the
  following inputs:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;The mask generated by Rasterization.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The “source color” for each pixel as determined by
      Shading.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The destination bitmap, which tells us the “destination
      color” for each pixel.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The transfer mode (&lt;code&gt;XferMode&lt;/code&gt;).&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;Once again, there are two sub-phases:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;
      An intermediate image is generated from the source image and
      destination image. For each each (x,y) coordinate the corresponding
      source and destination colors are passed to a function determined by
      the &lt;code&gt;XferMode&lt;/code&gt;. This function takes the source color and
      destination color and returns the color for the intermediate image's
      pixel at (x,y).&lt;/li&gt;

      &lt;p&gt;Note that the mask is &lt;em&gt;not&lt;/em&gt; used in this sub-phase. In
      particular, the source-alpha comes from the &lt;code&gt;Shader&lt;/code&gt;, and
      the destination alpha comes from the destination image.&lt;/p&gt;

      &lt;p&gt;If an &lt;code&gt;XferMode&lt;/code&gt; hasn't been set on the
      &lt;code&gt;Paint&lt;/code&gt; then the behavior is as though it was set to
      &lt;code&gt;PorterDuffXferMode(SRC_OVER)&lt;/code&gt;.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The second sub-phase takes the intermediate image, the
      destination image, and the mask as inputs and modifies the
      destination image. It does &lt;em&gt;not&lt;/em&gt; use the
      &lt;code&gt;XferMode&lt;/code&gt;.&lt;/p&gt;

      &lt;p&gt;The intermediate image is blended with the destination image
      through the mask. Blending means that each pixel in the
      destination image will become a weighted average (or equivalently,
      linear interpolation) of that pixel's original color and the
      corresponding pixel in the intermediate image. The opacity of the
      corresponding mask pixel is the weight of the intermediate color,
      and its transparency is the weight of the original destination
      color.

      &lt;p&gt;In other words, a pixel that is transparent (&lt;code&gt;0x00&lt;/code&gt;)
      in the mask will be left unaltered in the destination, a pixel
      that is opaque (&lt;code&gt;0xFF&lt;/code&gt;) in the mask will completely
      overwritten by the corresponding pixel in the intermediate image,
      and pixels that are partially transparent will result in a
      destination pixel color that is proportionately between its
      original color and the color of the corresponding intermediate
      image pixel.
    &lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;This is the final phase. The pipeline is now complete.

&lt;h5&gt;More on Porter Duff Transfer Modes&lt;/h5&gt;
  &lt;p&gt;The most commonly used transfer modes are instances of
  &lt;code&gt;PorterDuffXferMode&lt;/code&gt;. The behavior of a
  &lt;code&gt;PorterDuffXferMode&lt;/code&gt; is determined by its
  &lt;code&gt;PorterDuff.Mode&lt;/code&gt;. The documentation for each
  &lt;code&gt;PorterDuff.Mode&lt;/code&gt; (except &lt;code&gt;OVERLAY&lt;/code&gt;) shows
  the function that is applied to the source and destination colors
  to obtain the intermediate color. For example,
  &lt;code&gt;SRC_OVER&lt;/code&gt; is documented as:
  &lt;pre class=code&gt;[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc]&lt;/pre&gt;

  &lt;p&gt;This means:&lt;/p&gt;

&lt;pre class=code&gt;
Ra = Sa + (1 - Sa) * Da
Rr = Sr + (1 - Sa) * Dr
Rg = Sg + (1 - Sa) * Dg
Rb = Sb + (1 - Sa) * Db
&lt;/pre&gt;

  &lt;p&gt;Where &lt;code&gt;R&lt;var&gt;x&lt;/var&gt;&lt;/code&gt;, &lt;code&gt;S&lt;var&gt;x&lt;/var&gt;&lt;/code&gt;, and
  &lt;code&gt;D&lt;var&gt;x&lt;/var&gt;&lt;/code&gt; are the intermediate (result), source and
  destination values of the &lt;var&gt;x&lt;/var&gt; color component.&lt;/p&gt;

  &lt;p&gt;Some additional notes on the &lt;code&gt;PorterDuff.Mode&lt;/code&gt;
  documentation:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;The documentation uses “&lt;code&gt;Sc&lt;/code&gt;” and
      “&lt;code&gt;Dc&lt;/code&gt;” rather than describing each red, green, and
      blue component separately. This is because Porter Duff
      transfer modes always treat the non-alpha channels the same
      way and each of these channels is unaffected by all other
      channels except for alpha.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;&lt;code&gt;SRC_OVER&lt;/code&gt; and &lt;code&gt;DST_OVER&lt;/code&gt; are the
      only two modes that have the left hand side of this equation,
      “&lt;code&gt;Rc&lt;/code&gt;”, in their documentation. I'm guessing this
      inconsistency is a copy-and-paste error.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The alpha channel is always unaffected by non-alpha
      channels. That is, &lt;code&gt;Ra&lt;/code&gt; is always a function of
      only &lt;code&gt;Sa&lt;/code&gt; and &lt;code&gt;Da&lt;/code&gt;.&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The documentation for &lt;code&gt;ADD&lt;/code&gt; refers to a
      “&lt;code&gt;Saturate&lt;/code&gt;” function. This is just clamping to
      the range [0,1]. (I don't know why they use such an odd name
      for clamping, especially “saturation” usually refers to an
      entirely unrelated concept when talking about colors.)&lt;/p&gt;
    &lt;/li&gt;

    &lt;li&gt;
      &lt;p&gt;The definition of many of these modes, including
      &lt;code&gt;OVERLAY&lt;/code&gt;, can be found in the &lt;a href="http://www.w3.org/TR/2011/WD-SVGCompositing-20110315/#comp-op-property"&gt;SVG Compositing Specification&lt;/a&gt;.
      The Skia code actually links to (an older version of) this
      document. It has some good diagrams, too.&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;


  &lt;!-- TODO: Paint attrs used here --&gt;

  &lt;h5&gt;References&lt;/h5&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href="http://developer.android.com/reference/android/graphics/package-summary.html"&gt;The &lt;code&gt;android.graphics&lt;/code&gt; documentation&lt;/a&gt;.
    &lt;li&gt;&lt;a href="http://stackoverflow.com/questions/4560265/android-edit-bitmap-channels/4632210#4632210"&gt;This answer to “Android Edit Bitmap Channels” on Stack Overflow&lt;/a&gt;. Seeing this answer motivated me to learn more about how the pipeline actually works.
    &lt;li&gt;&lt;a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=graphics;hb=HEAD"&gt;The Android codebase&lt;/a&gt;. Since the documentation was so sparse and there didn't seem to be much information I looked to the source. My initial look stopped short when I realized everything was just a wrapper around “native” code.
    &lt;li&gt;&lt;a href="http://code.google.com/p/skia/wiki/"&gt;Skia documentation&lt;/a&gt;, particularly &lt;a href="http://code.google.com/p/skia/wiki/SkPaint"&gt;&lt;code&gt;SkPaint&lt;/code&gt;&lt;/a&gt;. Skia is the vast bulk of “native” (C++) code involved.
    &lt;li&gt;&lt;a href="http://stackoverflow.com/questions/5762727/how-do-the-pieces-of-androids-2d-canvas-drawing-pipeline-fit-together"&gt;“Stack Overflow: How do the pieces of Android's (2D) Canvas drawing pipeline fit together?”&lt;/a&gt;, a question I asked on Stack Overflow. One member of the Android team actually responded, but didn't really provide the details I was looking for.
    &lt;li&gt;&lt;a href="http://code.google.com/p/skia/source/browse/#svn%2Ftrunk"&gt;The Skia codebase&lt;/a&gt;. The code for &lt;code&gt;&lt;a href="http://code.google.com/p/skia/source/browse/trunk/src/core/SkCanvas.cpp"&gt;SkCanvas&lt;/a&gt;::drawPath&lt;/code&gt; is a good place to start.
    &lt;li&gt;&lt;a href="http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/#comp-op-property"&gt;SVG Compositing Specification: W3C Working Draft 30 April 2009&lt;/a&gt;. This document is mentioned in the Skia code.
    &lt;li&gt;&lt;a href="http://www.w3.org/TR/2011/WD-SVGCompositing-20110315/#comp-op-property"&gt; SVG Compositing Specification: W3C Working Draft 15 March 2011&lt;/a&gt;. This document supercedes the one mentioned in the Skia code. I believe the relevant bits still apply, but there's more detailed explanation and some good diagrams.
  &lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-6888811589008147369?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/pKCBEtiBHaM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/6888811589008147369/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=6888811589008147369" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/6888811589008147369?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/6888811589008147369?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/pKCBEtiBHaM/androids-2d-canvas-rendering-pipeline.html" title="Android's 2D Canvas Rendering Pipeline" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.xenomachina.com/2011/05/androids-2d-canvas-rendering-pipeline.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QDRHgyeip7ImA9WhZTFUs.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-5769644006161904915</id><published>2011-03-19T11:05:00.000-07:00</published><updated>2011-03-19T13:09:35.692-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-19T13:09:35.692-07:00</app:edited><title>What's good for the Twitter is good for the Apple</title><content type="html">&lt;p&gt;A lot of people have been &lt;a href="http://gigaom.com/2011/03/12/why-twitter-should-think-twice-about-bulldozing-the-ecosystem/"&gt;talking about Twitter's recent stance on third-party apps&lt;/a&gt;. I think &lt;a href="http://radar.oreilly.com/2011/03/twitter-developers.html"&gt;Mike Loukides of O'Reilly really hits the nail on the head&lt;/a&gt;:

&lt;blockquote&gt;
...you can't tell people where (or how) to innovate, and where not to. Innovation just doesn't work that way. The best way to prevent "think big" innovation from happening is to cut off the small ideas.
&lt;/blockquote&gt;

&lt;p&gt;Even &lt;a href="http://daringfireball.net/linked/2011/03/16/ingram-twitter"&gt;John Gruber, unabashed Apple fanboy, agrees&lt;/a&gt;:

&lt;blockquote&gt;
It’s not that I think Twitter is wrong in any moral sense to do whatever they want with their own API — it’s that I think they’d be foolish to do anything that dampens the diverse ecosystem of client software that has evolved around Twitter. They’re acting against their own self-interest, but apparently don’t realize it.
&lt;/blockquote&gt;

&lt;p&gt;Whether it's "moral" or not is open to debate. There does, however, seem to be general consensus that the changes in Twitter's policies are bad for developers, bad for users and in the long term bad for Twitter.

&lt;p&gt;The general form of the argument, which I wholeheartedly agree with, goes like this:
&lt;ol&gt;
&lt;li&gt;Artificially restricting developers hurts innovation. (See Loukides's quote, above.)
&lt;li&gt;Hurting innovation hurts users.
&lt;li&gt;Hurting users hurts the platform creator.
&lt;/ol&gt;

&lt;p&gt;These can be long term things, which makes them hard to measure. You can't just change your policy and see the effects overnight. For example, it might have taken years before a particular sort of ground-breaking third-party product would appear on a restriction-free platform, so in the short term having restrictions that forbid its existence might not appear to have significant detrimental effects. Likewise, most users won't miss the utility of a product they don't know exists, or even can exist. It generally takes a competing, less restricted, platform to come along before people really start to realize what they're missing. This is further slowed down by network effects.

&lt;p&gt;What's interesting is that this exact same chain of reasoning also applies to Apple and their App Store policies. Just as Twitter API clients should not "compete" with the official Twitter clients, apps for iOS are not allowed to compete with Apple products (or even other established iOS apps, to a degree). The iOS policies are actually far more restrictive on innovation than Twitter's policies, as the iOS policies largely forbid using Apple's APIs in any way that Steve Jobs didn't already imagine. "Think Different", indeed. (As an aside, I think Gruber is at least partially aware of the similarity, or he wouldn't have so carefully prefaced his statement with "It’s not that I think Twitter is wrong in any moral sense".)

&lt;p&gt;The parallels run even deeper. Even people who have come out in Twitter's defense on this issue often point out that Twitter's platform was in many ways built by the Twitter community (hash-tags and at-replies were being used by users before Twitter even had special support for them) and the large variety of Twitter clients also contributed to Twitter's success. For Twitter to suddenly institute draconian policies seems like a betrayal to some.

&lt;p&gt;If Twitter betrayed their users by being open at first and then closing up once they achieved popularity then Apple is just as guilty. Apple's trick was to stretch things out over a much longer time frame. Historically, Apple hardware was touted as being quite open. The Apple IIe was easily hackable both in a software and hardware sense. Apple's products weren't marketed as the "computer for the rest of us" just because they were easier or prettier than the competition, but also because they purportedly made it easier to create all sorts of things, including visual art, music and even computer programs. (I say "purportedly" because the Amiga and Atari ST were arguably just as good if not better when it came to certain sorts of creative work.) Remember Hypercard? A third-party equivalent to HyperCard wouldn't even be allowed given the current iOS App Store policies.

&lt;p&gt;One last thing to note is &lt;a href="https://groups.google.com/group/twitter-development-talk/browse_thread/thread/c82cd59c7a87216a"&gt;Twitter's stated reason for the policy change&lt;/a&gt;: 

&lt;blockquote&gt;
If there are too many ways to use Twitter that are inconsistent with one another, we risk diffusing the user experience.
&lt;/blockquote&gt;

&lt;p&gt;Hmmm, sounds like they're worried about "fragmentation".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-5769644006161904915?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/qEwE9u7Lazo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/5769644006161904915/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=5769644006161904915" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/5769644006161904915?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/5769644006161904915?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/qEwE9u7Lazo/whats-good-for-twitter-is-good-for.html" title="What's good for the Twitter is good for the Apple" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.xenomachina.com/2011/03/whats-good-for-twitter-is-good-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE4CRn8-cCp7ImA9Wx9RF00.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-7104390145300185445</id><published>2010-12-18T09:53:00.000-08:00</published><updated>2010-12-18T12:09:27.158-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-18T12:09:27.158-08:00</app:edited><title>PayPal stupidity</title><content type="html">&lt;p&gt;It seems that every year, while doing my Christmas shopping for relatives in Canada, I discover another major e-commerce site that doesn't understand that billing addresses and shipping addresses aren't necessarily in the same country.

&lt;p&gt;This year I was surprised to discover that PayPal, who you would think would have a clue, doesn't let you set a shipping address outside of your account's country. I was attempting to order an item from a Canadian website to be shipped to a Canadian address but because my PayPal account is a US account it will only let me create US shipping addresses.

&lt;p&gt;This issue isn't unknown to  PayPal, either, as evidenced by the &lt;a href="https://www.paypal-community.com/t5/How-to-use-PayPal/adding-a-shipping-address-in-canada/td-p/150"&gt;"adding a shipping address in canada"&lt;/a&gt; and the &lt;a href="https://www.paypal-community.com/t5/My-account-settings/How-do-I-use-a-foreign-address/td-p/38056"&gt;"How do I use a foreign address?"&lt;/a&gt; threads on the PayPal's Community Help forums. This appears to be the official response:

&lt;blockquote&gt;It is not possible to add an foreign address to your PayPal account within PayPal. You can open a new account with your Canadian address and Canadian financial information.
&lt;/blockquote&gt;

&lt;p&gt;Given that this appeared to be my only available  option  I decided to try and set up a Canadian PayPal account.  This required that I come up with a new e-mail address for the account, since PayPal uses a single namespace for all accounts (arguably the right thing to do, but it doesn't interact well with the boneheaded policy of requiring a separate account for each  country). Luckily I have an unlimited supply of  e-mail addresses.

The sign-up process then wants you to enter banking or credit card information. Of course, they are restricted to the country that you have selected, in my case Canada. I do not have a Canadian bank account or credit card (anymore). I was about to give up, but then I realized that I could just click on “my account” and bypass this step entirely. To complete my purchase I then:

&lt;ol&gt;
&lt;li&gt; Attempted to purchase with the merchant. This was just to find out the exact amount I was going to  be charged.
&lt;lI&gt; Transferred funds from my US PayPal account to my Canadian PayPal account by “sending money” to myself. Having a second browser open was useful for this step. Thankfully, I was able to choose which currency to use in my US PayPal account so I didn't need to do any currency conversions by hand.
&lt;li&gt; Waited several minutes for the funds to show up in my Canadian PayPal account.
&lt;lI&gt; Actually purchased the item from the merchant.
&lt;/ol&gt;

&lt;p&gt;A few minutes (!)  after purchasing the item PayPal actually called me on the phone. They wanted to make sure that I "still had control of my account", referring to the new account I had just created. I told him I did, and then I mentioned the annoyance of having to create a second account just so I could ship to another country.   They confirmed that what I did was basically the only option, and said the reason for this is to make sure that each account complies with the laws of the country that it is  associated with. This seems bogus. I could &lt;em&gt;maybe&lt;/em&gt; understand only allowing banking information from a single country per account, but there's no good reason to put the same restriction on shipping information. PayPal does nothing with the shipping information except pass it along to the seller.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-7104390145300185445?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/44TG_Nql7vA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/7104390145300185445/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=7104390145300185445" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/7104390145300185445?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/7104390145300185445?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/44TG_Nql7vA/paypal-stupidity.html" title="PayPal stupidity" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.xenomachina.com/2010/12/paypal-stupidity.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEUCSXoyfyp7ImA9Wx5XEEo.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-4708009230736429636</id><published>2010-09-09T11:37:00.000-07:00</published><updated>2010-09-09T16:51:08.497-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-09T16:51:08.497-07:00</app:edited><title>iOS Developer Agreement: Too Little Too Late</title><content type="html">&lt;p&gt;It looks like Apple might be regaining some of their sanity given the
recent update to the iOS  developer agreement.

&lt;h5&gt;Compilers&lt;/h5&gt;

&lt;p&gt;Section 3.3.1 has been updated to only restrict the use of private
APIs. This is a perfectly reasonable restriction. The clause which
stated that “applications must be originally written in Objective-C” (in
my mind, the most offensive part of the iOS developer agreement) has
been removed. I'm very glad to see it's gone.

&lt;h5&gt;Interpreters&lt;/h5&gt;

&lt;p&gt;They also updated section 3.3.2, the “no interpreters” section. The
language has changed but the meaning apparently hasn't:

&lt;blockquote&gt;
An Application may not download or install executable code. Interpreted
code may only be used in an Application if all scripts, code and
interpreters are packaged in the Application and not downloaded. The
only exception to the foregoing is scripts and code downloaded and run
by Apple’s built-in WebKit framework.
&lt;/blockquote&gt;

&lt;p&gt;The old version of this rule was confusing and unclear, and the new
version, despite being less verbose, still leaves a lot open to
interpretation. For starters,  what does “install” mean in this context?
If the user of the app manually constructs the executable code, is that
allowed or not?

&lt;p&gt;The definition of “executable code” isn't entirely clear either. My
inclination is to assume that this means a Turing complete language, but
one could argue that there are even non-Turing complete languages that
count as “executable code”. For example, I wonder if an iOS port of the
classic 8-bit educational game &lt;a
href="http://en.wikipedia.org/wiki/Rocky's_Boots"&gt; Rocky's Boots&lt;/a&gt;
would run afoul of this rule. In the game you would construct machines
out of various bits including Boolean logic gates and then use these
machines to solve various puzzles. “Running” the machines in the game
requires the interpreting of executable code.

&lt;p&gt;Either way, the restrictions imposed by this section probably don't
affect as many developers as the old 3.3.1 restrictions did. However, in
some ways this rule is actually worse. The old 3.3.1 only restricted how
one could build apps but it didn't really limit the types of apps that
one could build. The no interpreters rule, however, actually makes it
impossible to implement several classes of useful software on the iOS
platform, including:

&lt;ul&gt;
&lt;li&gt; Web browsers that interpret JavaScript on the client.
&lt;li&gt; Emulators of legacy platforms, like 8-bit computers or old game consoles, that allow the user to run their existing software (e.g.: game ROMs, etc.).
&lt;li&gt; Educational development tools like &lt;a href="http://computinged.wordpress.com/2010/04/15/apple-removes-scratch-from-ipadiphoneitouch/"&gt;Scratch&lt;/a&gt;.
&lt;li&gt; Mathematics software like Mathematica or Maple.
&lt;li&gt; Electronic circuit simulators.
&lt;li&gt; PostScript or TeX viewers (both are Turing complete languages).
&lt;/ul&gt;

Apple implies that the reason for this rule is “security”:

&lt;blockquote&gt;
In particular, we are relaxing all restrictions on the development tools
used to create iOS apps, as long as the resulting apps do not download
any code. This should give developers the flexibility they want,
&lt;strong&gt;while preserving the security we need&lt;/strong&gt;. [emphasis mine]
&lt;/blockquote&gt;


&lt;p&gt;It's a pretty sad to see Apple is falling back on “security” as an
excuse for limiting what customers can do with the products that they
purchased. This is the same thing Sony did a few months ago when they
removed “install other OS”  (an advertised feature) from the PlayStation
3. In Sony's case the security issue had to do with their DRM. In other
words, it wasn't a customer's security they were concerned for, but
their profit's. One has to wonder if Apple has similar motives.  An
interpreter acts as a sandbox, so un-trusted code execution there is
generally not as big a deal as arbitrary native code execution, as might
result from &lt;a
href="http://digdog.tumblr.com/post/894317027/jailbreak-with-pdf-flatedecode-filter"&gt;a
buffer overflow or similar bug in native code&lt;/a&gt;.  Last I checked,
Apple wasn't prohibiting string manipulation in native apps.

&lt;h5&gt;Analytics&lt;/h5&gt; &lt;p&gt;Like 3.3.1, section 3.3.9, the privacy and
analytics section, has also changed for the better. The language that
specifically forbade Google's AdMob is gone, meaning developers can
decide which advertising platform to use.

&lt;h5&gt;Why?&lt;/h5&gt;

&lt;p&gt;Apple says in their announcement:

&lt;blockquote&gt;
We have listened to our developers and taken much of their feedback to
heart. Based on their input, today we are making some important changes
to our iOS Developer Program license in sections 3.3.1, 3.3.2 and 3.3.9
to relax some restrictions we put in place earlier this year.
&lt;/blockquote&gt;

&lt;p&gt;Apple clearly didn't anticipate the backlash that would be caused by
3.3.1 when the “originally in Objective-C” clause was added. Not only
were developers angered by that rule, but since its addition, people
have been looking much more closely at what's in the developer
agreement. Apple doesn't want this scrutiny as it brings to light
already existing ridiculous rules, like 3.3.2, and makes people more
likely to question Apple's motives when new rules are introduced, like
3.3.9. It also made many developers (and tech savvy users) who liked
Apple (myself included) re-evaluate whether this was really a company
they wanted to purchase products from or develop for.

&lt;p&gt;I think there's also a possibility that the recent changes to 3.3.9 were made in order to avoid legal  issues.

&lt;p&gt;Neither of these are really great reasons for Apple to change their
behavior. I think Steve Jobs preferred the older set of rules, but it
became clear that developers, and potentially even the law, wouldn't
stand for them.

&lt;h5&gt;To iOS or not to iOS&lt;/h5&gt;

&lt;p&gt;The current developer agreement is a lot closer in meaning to the
pre-iPad developer agreement. Back when the iPad came out I had
considered getting one so that I could experiment with  developing for
iOS. I gave up on that plan when the “originally in Objective-C” rule
was added. So now that the rules are pretty much back where they were,
am I going to get an iOS device?

&lt;p&gt;Probably not. Apple has lost my trust, and in order to win it back
they'll have to do more than just change things back to the way they
were. For starters, I'd like to see them make a rule for themselves that
the developer agreement will apply not just to third-party developers,
but also to Apple's own iOS apps. For existing Apple apps that violate
the rules they can then choose to revise the agreement for everyone, fix
the app, remove the app. Apple already has an advantage over
third-party developers, so for them to impose rules whose only apparent
purpose is to strengthen Apple's advantage over third-party developers
is reprehensible.  I'm looking at you Safari.

&lt;p&gt;Better yet would be to make it possible for people to distribute native
iOS apps without going through the App Store. I'd care a lot less about
the App Store policies are if there were other ways to get apps on
iDevices. I'm fine with this being a setting users need to enable (as it
is on Android devices), but requiring that the user "jailbreak" their
device to get such basic functionality is not acceptable.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-4708009230736429636?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/p9ABXoEZaUg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/4708009230736429636/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=4708009230736429636" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/4708009230736429636?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/4708009230736429636?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/p9ABXoEZaUg/ios-developer-agreement-too-little-too.html" title="iOS Developer Agreement: Too Little Too Late" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.xenomachina.com/2010/09/ios-developer-agreement-too-little-too.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYHRXs-fSp7ImA9WxFSE0o.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-419035507744531798</id><published>2010-04-13T11:41:00.000-07:00</published><updated>2010-04-15T17:18:54.555-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-15T17:18:54.555-07:00</app:edited><title>Steve Jobs and iPhone Developer Agreement Section 3.3.1</title><content type="html">&lt;p&gt;On reading &lt;a href="http://www.taoeffect.com/blog/2010/04/steve-jobs-response-on-section-3-3-1/"&gt;Steve Jobs’s response on Section 3.3.1&lt;/a&gt;, there are two things that struck me:

&lt;p&gt;First, like &lt;a href="http://daringfireball.net/2010/04/why_apple_changed_section_331"&gt;John Gruber&lt;/a&gt;, he doesn't seem to understand the implications of the new text in section 3.3.1. In particular, it says:

&lt;blockquote&gt;
Applications must be originally written in Objective-C, C, C++, or JavaScript as executed by the iPhone OS WebKit engine...
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;This does not prohibit the use of cross-platform toolkits&lt;/strong&gt;, which is what he seems to think it does. There are many cross platform toolkits that are written in C and C++. What it does prevent (as I mentioned in &lt;a href="http://www.xenomachina.com/2010/04/iquality.html"&gt;my post yesterday&lt;/a&gt;) is using other &lt;em&gt;languages&lt;/em&gt;, something Jobs's argument completely ignores. That he wouldn't understand what the the text of this section actually means is pretty disturbing.

&lt;p&gt;The other thing that struck me is his reason for wanting to keep cross-platform toolkits off of the iPhone OS. He's afraid that cross platform toolkits will make it easier to write apps that exist on both iPhone OS and competing platforms, and that &lt;strong&gt;if the same apps exist on other platforms people won't have a reason to purchase iPhone OS products&lt;/strong&gt;. This suggests a lack of faith in the technology of the iPhone OS platform.

&lt;p&gt;With the changes Apple has made to section 3.3.1 they are setting a very bad precedent. People have complained about the behavior of other platform owners in the past, including IBM, Microsoft, and the various game console manufacturers, for imposing all sorts of nasty restrictions on developers, but none of them have ever gone as far as to forbid the use of either toolkits or source-to-source translation. That Apple would do this suggests to me both a lack of respect for developers (and hence, a failure to understand that without third party developers a platform is basically worthless), and also a lack of faith in their own platform's merits.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-419035507744531798?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/PzqqZthnmIg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/419035507744531798/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=419035507744531798" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/419035507744531798?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/419035507744531798?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/PzqqZthnmIg/steve-jobs-and-iphone-developer.html" title="Steve Jobs and iPhone Developer Agreement Section 3.3.1" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.xenomachina.com/2010/04/steve-jobs-and-iphone-developer.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYFRHs_cCp7ImA9WxFSEEQ.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-7636439916257386933</id><published>2010-04-12T10:45:00.000-07:00</published><updated>2010-04-12T11:48:35.548-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-12T11:48:35.548-07:00</app:edited><title>iQuality™</title><content type="html">&lt;p&gt;iPhone/iPad app developers:
&lt;ul&gt;
&lt;li&gt;Pay $99 for the privilege to improve Apple's platform by developing new apps for it.&lt;/li&gt;
&lt;li&gt;Have only one option for distributing their software, which involves paying Apple 30% of their revenue.&lt;/li&gt;
&lt;li&gt;Risk having their apps disapproved after the app has already been developed. Reasons for disapproval include:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://techcrunch.com/2009/07/27/apple-is-growing-rotten-to-the-core-and-its-likely-atts-fault/"&gt;Having functionality that overlaps with existing or future Apple products.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.joystiq.com/2009/09/08/iphone-commodore-64-app-removed-from-app-store/"&gt;Including an interpreter of any sort as part of the app.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that initial approval doesn't mean you won't &lt;a href="http://www.mobilecrunch.com/2009/08/03/apple-bans-app-stores-3rd-most-prolific-developer/"&gt;get pulled from the App Store later on because Apple decided to change the rules&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;And now, due to Apple's recent changes to section 3.3.1 of the iPhone developer agreement, developers must use an Apple prescribed programming language: Objective-C, C or C++. This new rule means that &lt;a href="http://lambda-the-ultimate.org/node/3905"&gt;you can't even use a compiler that emits one of the allowed languages&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img border="0" align="right" alt="" src="http://xenomachina.com/images/JobsSealOfQuality.png"&gt;Apple apologists often claim that these hurdles improve the user experience by acting as a sort of quality control. Sometimes a comparison to the "&lt;a href="http://en.wikipedia.org/wiki/Nintendo"&gt;Nintendo Seal of Quality&lt;/a&gt;" is made.&lt;/p&gt;
&lt;p&gt;The Nintendo Seal of Quality was developed by Nintendo in the 80's as a response to the "&lt;a href="http://en.wikipedia.org/wiki/North_American_video_game_crash_of_1983"&gt;video game crash&lt;/a&gt;" that happened earlier that decade. One of the reasons for the crash was a glut of crappy games that made people wary of buying games in general, and eventually lead to the (albeit, prolonged) death of Atari and much of the video game industry at the time.&lt;/p&gt;
&lt;p&gt;A recent example of this sort of argument was &lt;a href="http://daringfireball.net/2010/04/why_apple_changed_section_331"&gt;John Gruber's Daring Fireball post on the change to section 3.3.1 of the iPhone developer agreement&lt;/a&gt;. In it he says:&lt;/p&gt;
&lt;blockquote&gt;On the one side, this rule should be good for quality. Cross-platform software toolkits have never — ever — produced top-notch native apps for Apple platforms. Not for the classic Mac OS, not for Mac OS X, and not for iPhone OS. Such apps generally have been downright crummy.
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;The iPhone OS Kindle app is excellent, a worthy rival in terms of experience to Apple’s own iBooks. The Mac Kindle app is a turd that doesn’t look, feel, or behave like a real Mac app. The iPhone OS Kindle app is a native iPhone app, written in Cocoa Touch. The Mac Kindle app was produced using the cross-platform Qt toolkit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are a few serious flaws with his reasoning here.&lt;/p&gt;
&lt;p&gt;First of all, there's the question of whether cross-platform toolkits have ever produced top-notch native apps for Apple platforms. (The "for Apple platforms" qualifier here seems superfluous to me, but I'll let it go.) He claims that they never have, but I doubt that he has actually performed a exhaustive survey of software for Apple platforms to determine this.&lt;/p&gt;
&lt;p&gt;Virtually all software with a graphical user interface that exists on more than one platform makes use of some sort of cross-platform toolkit. While it's easy to spot a badly ported application, applications that have been ported properly are indistinguishable from ones that were written "natively". It wouldn't surprise me at all if a number of the "top-notch apps" Gruber uses actually do use a cross-platform toolkit without his knowledge.&lt;/p&gt;
&lt;p&gt;In fact, Firefox is built using &lt;a href="https://developer.mozilla.org/en/XPCOM"&gt;XPCOM&lt;/a&gt;, a cross-platform toolkit (the "XP" actually stands of "cross-platform"). One could even argue that &lt;a href="http://webkit.org/"&gt;WebKit&lt;/a&gt; is a cross-platform toolkit as well (though it's specific to HTML rendering, rather than being a general GUI toolkit). So if Gruber is right then none of Safari, Google Chrome, or Firefox are "top-notch native apps".&lt;/p&gt;
&lt;p&gt;But for the sake of argument, let's say Gruber is right and that cross-platform toolkits really have never produced top-notch native apps for any Apple platform. It doesn't matter, because the "cross-platform toolkits" argument is a complete red herring. Take a close look at the text of section 3.3.1:&lt;/p&gt;
&lt;blockquote&gt;Applications must be originally written in Objective-C, C, C++, or JavaScript as executed by the iPhone OS WebKit engine, and only code written in C, C++, and Objective-C may compile and directly link against the Documented APIs (e.g., Applications that link to Documented APIs through an intermediary translation or compatibility layer or tool are prohibited).&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;There's no talk about toolkits here &amp;mdash; it's talking about &lt;em&gt;languages&lt;/em&gt;&lt;/strong&gt;. There is nothing about this rule that prevents you from using a cross-platform toolkit &lt;em&gt;provided that it's in Objective-C, C, or C++&lt;/em&gt;. Qt is a C++ toolkit, so if it weren't for the fact that it relies on a pre-processor (which is unusual for cross-platform GUI toolkits) then there's nothing about section 3.3.1 that says you couldn't port a QT app, like the Kindle application, to the iPhone OS.&lt;/p&gt;
&lt;p&gt;Some of the things section 3.3.1 actually &lt;em&gt;will&lt;/em&gt; prevent are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writing apps in other languages that teams may be more proficient in (Lisp, OCaml, Erlang, Scala, Python, Ruby, Java C#) &lt;em&gt;even if C, C++ or Objective-C is used as an intermediate step&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Hand-coding inner loops in assembly code (a common practice in games).&lt;/li&gt;
&lt;li&gt;Using domain-specific languages for specific tasks. For example:
&lt;ul&gt;
&lt;li&gt;Using Lex, YACC, &lt;a href="http://www.antlr.org/"&gt;ANTLR&lt;/a&gt; or any other compile time parser generator. (for example: to produce a parser for parsing data fetched from the web)&lt;/li&gt;
&lt;li&gt;Using a compile-time template engine. (for example: to generate HTML for emails)&lt;/li&gt;
&lt;li&gt;Using &lt;a href="http://www.gnu.org/software/gperf/"&gt;gperf&lt;/a&gt; for perfect hashing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using code transformation tools. For example:
&lt;ul&gt;
&lt;li&gt;Internationalization tools that perform build-time string extraction and reinsertion.&lt;/li&gt;
&lt;li&gt;Macro processors like &lt;a href="http://en.wikipedia.org/wiki/M4_%28computer_language%29"&gt;m4&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using existing libraries written in other languages. For example, many numerical libraries are written in Fortran.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a software engineer, I'd be very surprised if any of the above had a correlation with lower quality applications. On the contrary, it tends to be the more proficient software developers that use a wider variety of tools, including ones like those listed above. (Luckily, actually identifying apps that violate this policy would be very hard, if not impossible, without the source code. If it weren't for this fact, we'd likely see a number of existing apps get pulled off of the store.)&lt;/p&gt;
&lt;p&gt;Other App Store policies have similar problems. For example, it seems extremely unlikely that containing an interpreter is positively correlated with low quality. Instead, the no interpreters rule locks out a certain class of app. You cannot have a &lt;a href="http://tsdgeos.blogspot.com/2008/02/postscript-is-turing-complete.html"&gt;PostScript&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/TeX"&gt;TeX&lt;/a&gt; viewer without an interpreter, for example. Emulators for old machines are also affected. (Technically an emulator &lt;em&gt;is&lt;/em&gt; an interpreter, though Apple seems to allow emulators as long as they don't let the user supply new code to be emulated/interpreted.) Even Safari runs afoul of this rule given that it includes a JavaScript interpreter, but of course Apple doesn't need to follow their own rules.&lt;/p&gt;
&lt;p&gt;But never mind what I think. Even if the intent behind these policies was to keep low quality apps out of the App Store (which is hard to believe) the fact that the App Store is full of crappy apps is evidence enough that they've failed in this regard. These policies don't work to keep low quality apps out. The various high profile examples of apps that have been either disallowed, left in review limbo or lobotomized also speak strongly against the notion that Apple's policies have anything to do with improving things for their users.&lt;/p&gt;
&lt;p&gt;So to sum up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Many of Apple's policies don't appear to have been designed to improve quality.&lt;/li&gt;
&lt;li&gt;If they were designed to improve quality, the large number of garbage apps on the App Store make it clear that they aren't really working.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-7636439916257386933?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/rhpp-oxzwvk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/7636439916257386933/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=7636439916257386933" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/7636439916257386933?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/7636439916257386933?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/rhpp-oxzwvk/iquality.html" title="iQuality™" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.xenomachina.com/2010/04/iquality.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcGRXg6eip7ImA9WxNRF0Q.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-3074352766761085394</id><published>2009-09-12T15:12:00.000-07:00</published><updated>2009-09-12T16:10:24.612-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-12T16:10:24.612-07:00</app:edited><title>AT&amp;T Wireless: getting off to a bad start</title><content type="html">&lt;p&gt;I have an unlocked Android (G1) phone. Miranda would like an iPhone, so we've decided to switch to AT&amp;amp;T. She can get her iPhone and I'll put a SIM card into my Android phone.  (I'm okay with 3G not working on the Android.)

&lt;p&gt;My initial thought was to get a free phone from AT&amp;amp;T,  But then it occurred to me that this would use up my phone subsidy for two years. I tried adding a second line to a family plan on AT&amp;amp;T's website and then removing the phone, but it wouldn't let me complete the order.  I decided to chat with their customer support. Here's the conversation I just had with them:

&lt;blockquote&gt;
&lt;br&gt;▶Thank you for your patience! An AT&amp;T sales representative will be with you shortly.
&lt;br&gt;▶You are now chatting with Tiffany, an AT&amp;T sales representative.
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: Hello, Laurence.
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: Welcome to AT&amp;T online Sales support.  How may I assist you with placing your order today?
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: I want to get a family plan with 2 lines, but only want one phone, as I already have an unlocked GSM phone that I would like to use.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: Is there a way to do that?
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: I will be glad to assist you.
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: You may order a free phone so that you can  get the SIM Card from that phone.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: Im thinking about if the phone I'm using breaks or something, I'd probably rather get a better phone than just using the free phone. By getting the free phone I'm essentially locking myself out of a subsidized phone for 2 years though, right?
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: (It also seems like a waste to get a phone I'll never actually use.)
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: If you would like, you may purchase a SIM Card in the store for $25, but once you sign the contract, it will be 2 years before a upgrade discount is given.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: I have to wait 2 years to get a subsidized phone even if I don't get any phone to begin with?
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: Yes, since the terms are for two years.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: So there isn't any way to get the contract, but hold off on getting a phone until later unless I want to either wait 2 years, or pay the full, unsubsidized, price?
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: You may set the line up online as a individual plan, then when the phone arrives, you may merge the lines in the store as a family plan and inquire on later getting a phone at a discounted price if you are not purchasing a phone at the time.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: I'm a bit confused.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: Let's pretend I was just getting an individual plan.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: Is there a way I could get an individual plan with just a SIM card (no phone), and then a few months later get a phone at the same price as if I'd gotten it when I got the plan?
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: The SIM Card will provide you with service, the service is for two years, it is not necessarily the phone, it is the contract that you are agreeing to which determines your discounts on the phones.
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: You term will still be 2 years, making it that amount of time to upgrade to a new phone for a discount.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: I understand that. I'm just surprised that I have to use the discount at the beginning of the contract or I lose it.
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: I do understand.
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: I do recommend that you visit your local AT&amp;T Store to inquire on the procedure of using it at a later time.
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: Ok, I'll try that. Thanks.
&lt;br&gt;&lt;b&gt;Tiffany&lt;/b&gt;: It was my pleasure, may I be of further assistance today?
&lt;br&gt;&lt;b&gt;Laurence&lt;/b&gt;: No, that's all I wanted to ask about.
&lt;/blockquote&gt;

&lt;p&gt;The part that's especially ridiculous to me is that AT&amp;amp;T is essentially forcing me to waste &lt;em&gt;their&lt;/em&gt; money. If they would let me save my subsidized phone discount for later, chances are I wouldn't even use it. Instead they're telling me I &lt;em&gt;have&lt;/em&gt; to use it now, which means I definitely will and on a phone that I'm not even going to use. I don't even want to think about what that $25 charge for a SIM card that Tiffany mentioned is about.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-3074352766761085394?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/GwOB5hzMzHQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/3074352766761085394/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=3074352766761085394" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/3074352766761085394?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/3074352766761085394?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/GwOB5hzMzHQ/at-wireless-getting-off-to-bad-start.html" title="AT&amp;amp;T Wireless: getting off to a bad start" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.xenomachina.com/2009/09/at-wireless-getting-off-to-bad-start.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C04HSX88cCp7ImA9WxdVGE4.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-7641007477672735222</id><published>2008-07-23T09:30:00.000-07:00</published><updated>2008-07-23T09:52:18.178-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-07-23T09:52:18.178-07:00</app:edited><title>GXP Now Open Source</title><content type="html">&lt;a href="http://gxp.googlecode.com/"&gt;Google XML Pages (GXP)&lt;/a&gt; is now open source! See  &lt;a href="http://google-opensource.blogspot.com/2008/07/google-xml-pages-functional-markup.html"&gt;the announcement on the Google Open Source Blog&lt;/a&gt; for more info.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-7641007477672735222?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/Nqz_mdvHUQU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/7641007477672735222/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=7641007477672735222" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/7641007477672735222?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/7641007477672735222?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/Nqz_mdvHUQU/gxp-now-open-source.html" title="GXP Now Open Source" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>7</thr:total><feedburner:origLink>http://www.xenomachina.com/2008/07/gxp-now-open-source.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkEASXY_fip7ImA9WxdVFkw.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-4171143272431987652</id><published>2008-07-18T12:11:00.000-07:00</published><updated>2008-07-20T22:37:28.846-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-07-20T22:37:28.846-07:00</app:edited><title>Speaking at OSCON 2008</title><content type="html">&lt;a href="http://conferences.oreilly.com/oscon"&gt;
&lt;img align=right src="http://assets.en.oreilly.com/1/event/12/oscon2008_banner_125x125.gif" width="125" height="125"  border="0"  alt="OSCON 2008" title="OSCON 2008"  /&gt;
&lt;/a&gt;
I'll be speaking at O'Reilly's Open Source Convention (OSCON) next week about &lt;a href="http://en.oreilly.com/oscon2008/public/schedule/detail/3501"&gt;Google XML Pages&lt;/a&gt;. GXP is an XML-based templating system that I originally built almost 7 years ago, and have been working on (mostly 20%-time) since then. It's hard to believe, but it's finally being open-sourced.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-4171143272431987652?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/NFlUS6La504" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/4171143272431987652/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=4171143272431987652" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/4171143272431987652?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/4171143272431987652?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/NFlUS6La504/speaking-at-oscon-2008.html" title="Speaking at OSCON 2008" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://www.xenomachina.com/2008/07/speaking-at-oscon-2008.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYAQX4_fip7ImA9WxZXEkw.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-5723513548299330241</id><published>2008-02-28T08:36:00.000-08:00</published><updated>2008-02-28T09:02:20.046-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-28T09:02:20.046-08:00</app:edited><title>UI Design in Sunshine</title><content type="html">&lt;p&gt;I just saw &lt;a href="http://www.imdb.com/title/tt0448134/"&gt;Sunshine&lt;/a&gt;, and while going through the extras I was pleasantly surprised by this short video about the design of the user interfaces on board the ship in the movie. The whole video is pretty interesting, but the part that made me literally laugh out loud starts just after 1:47.

&lt;p align="center"&gt;&lt;object width="425" height="355"&gt;&lt;param name="movie" value="http://www.youtube.com/v/0bqINrwoMbI"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/0bqINrwoMbI" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-5723513548299330241?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/1El4RmqGbgY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/5723513548299330241/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=5723513548299330241" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/5723513548299330241?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/5723513548299330241?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/1El4RmqGbgY/ui-design-in-sunshine.html" title="UI Design in Sunshine" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.xenomachina.com/2008/02/ui-design-in-sunshine.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkIGR3Y5cCp7ImA9WxZTGUU.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-6664660747110845073</id><published>2008-01-21T21:16:00.000-08:00</published><updated>2008-01-21T22:48:46.828-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-21T22:48:46.828-08:00</app:edited><title>Flickr Photo Page Bookmarklet</title><content type="html">&lt;p&gt;Occasionally I come across images that are hosted by &lt;a href="http://www.flickr.com/"&gt;Flickr&lt;/a&gt;, but which don't have a link back to the photo page on Flickr. This seems to be especially common on message boards and image bookmarking sites like &lt;a href="http://ffffound.com/"&gt;FFFFound&lt;/a&gt;. Being able to get to the Flickr photo page is nice for a number of reasons: you can see other photos by the same author, see the metadata, notes, comments, others sizes, etc.

&lt;p&gt;A Flickr photo URL looks something like this:

&lt;pre class=code&gt;  http://farm2.static.flickr.com/1407/1085850486_23589455e2.jpg&lt;/pre&gt;

&lt;p&gt;Flickr &lt;a href="http://www.flickr.com/services/api/misc.urls.html"&gt;has some nice documentation for their URLs&lt;/a&gt;. The URL above is of the form &lt;code&gt;http://farm&lt;var&gt;farm-id&lt;/var&gt;.static.flickr.com/&lt;var&gt;server-id&lt;/var&gt;/&lt;var&gt;id&lt;/var&gt;_&lt;var&gt;secret&lt;/var&gt;.jpg&lt;/code&gt;, so the photo ID is 1085850486 and the "secret" is 23589455e2. It's possible to find the photo page by using &lt;a href="http://www.flickr.com/services/api/explore/?method=flickr.photos.getInfo"&gt;Flicker's API call  "flickr.photos.getInfo"&lt;/a&gt;, which returns XML containing the photo ID. That's tedious, and won't work in a bookmarklet.

&lt;p&gt;Some more searching turned up the &lt;code&gt;http://flickr.com/photo.gne?id=&lt;var&gt;id&lt;/var&gt;&lt;/code&gt; URL. I can't find any official documentation for this, only &lt;a href="http://www.flickr.com/help/forum/41252/"&gt;some posts in the Flick help forums&lt;/a&gt;. It appears to work though, and it wasn't too hard to turn it into a bookmarklet.

&lt;p&gt;To install the bookmarklet, just drag the following link to your bookmarks or toolbar.

&lt;p&gt;&lt;a href="javascript:document.location='http://flickr.com/photo.gne?id='+document.location.pathname.replace(/_.*/,'').replace(/.*\//,'')"&gt;[Flickr photo page]&lt;/a&gt;

&lt;p&gt;To use the bookmarklet, first make sure that the photo is the only thing open in the browser tab/window. In other words, the URL in the address bar should be for the .jpg hosted on flickr.com. If the photo is embedded in a page you'll generally need to select "View Image" (or equivalent) from the context menu. (FFFFound is an exception: it caches the images, so you'll instead need to click on the image to be taken to the original) One you're on the image, just click the bookmarklet to be sent to the Flickr photo page.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-6664660747110845073?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/oBb6tdm4EEQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/6664660747110845073/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=6664660747110845073" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/6664660747110845073?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/6664660747110845073?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/oBb6tdm4EEQ/flickr-photo-page-bookmarklet.html" title="Flickr Photo Page Bookmarklet" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.xenomachina.com/2008/01/flickr-photo-page-bookmarklet.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4NR3Y_eyp7ImA9WB5XEk8.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-116828956267846360</id><published>2007-07-11T23:09:00.000-07:00</published><updated>2007-07-11T23:16:36.843-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-07-11T23:16:36.843-07:00</app:edited><title>Rant: Bad Internationalization</title><content type="html">&lt;p&gt;A lot of websites don't seem to "get" the fact that the Internet is &lt;em&gt;international&lt;/em&gt;. I'm not talking about things like being translated into multiple languages or outputting content in UTF-8 (in this post). What really annoys me are sites that seem to actually go out of their way to make life difficult for international users and/or customers.

&lt;p&gt;Credit cards have the nice property that they work internationally. If someone accepts Visa or Mastercard they'll normally accept those cards no matter what country the cards originate in or what currency the account is in. Currency conversion is handled automatically by the credit card issuing bank. A number of websites seem to be incapable of dealing with this properly, however.

&lt;h2&gt;Air Canada&lt;/h2&gt;
&lt;p&gt;An example I recently encountered is Air Canada's website. I was going to purchase some plane tickets for some family in Canada. After going through the ordeal of finding flights that met the various constraints I got to the payment page. I then entered my credit card number and most of my billing address only to find that "United States" wasn't in the country selector. "Is it listed as America? USA?" I thought to myself. Nope. It just wasn't there.

&lt;p&gt;After a few minutes of trying to figure out what was up I noticed a paragraph in the sidebar that helpfully pointed out that the United States, as well as a handful of other countries, had been &lt;em&gt;intentionally left out of the available options&lt;/em&gt; based on the country selection I had (apparently) made when I first visited the site. The only rationale I can think of for this is that it's a way to charge customers in different countries different prices. Blech.

&lt;h2&gt;Dell&lt;/h2&gt;
&lt;p&gt;Another incident happened back around Christmas. I was going to order a present for my father, who lives in Canada, from Dell. I first tried using &lt;code&gt;dell.com&lt;/code&gt;, but it wouldn't let me ship to Canada. I then tried &lt;code&gt;dell.ca&lt;/code&gt;, but the billing page had the country field hard-coded to Canada. My US-based credit card wasn't going to work.

&lt;p&gt;After failing with both websites, I decided to try and get in touch with a human. "I'll just call the number on &lt;code&gt;dell.ca&lt;/code&gt;", I thought. The number is 1-800-WWW-DELL (phone over HTTP?), and it's the same number on &lt;code&gt;dell.com&lt;/code&gt;. Calling the number from the US connected me to Dell US, not Dell Canada, and they wouldn't ship to Canada. I asked if they could connect me to Dell Canada, and the sales person suggested that I just have the present delivered to myself, in California, and then I could ship it to Canada myself.

&lt;p&gt;This idea is all kinds of terrible because it would mean more work on my part, be more expensive, take longer for the gift to arrive, and it'll also mean that the recipient would have have to pay GST -- not a very nice Christmas present. (GST is a tax applied to items as they cross into Canada.) By ordering from Dell Canada I could pay the GST, rather than the recipient having to pay it, the shipping would be direct (and presumably from a warehouse in Canada), and would be included in the price.

&lt;p&gt;Eventually I was able to convince the Dell US salesperson to give me a real phone number for Dell Canada sales, and I was able to order the gift. Still, this would've been far less painless if &lt;code&gt;dell.ca&lt;/code&gt; just let me choose the billing country for my credit card. Better yet, &lt;code&gt;dell.com&lt;/code&gt; should allow shipping to Canada by forwarding my order to Dell Canada for me.

&lt;p&gt;With the Dell example I think the reason for this screwup was less malice, more oversight/ignorance/stupidity. However, in some ways it's even more sad that even Dell, a company known for being relatively 'net savvy, can't seem to get this right.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-116828956267846360?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/wZxs84EmBNs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/116828956267846360/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=116828956267846360" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116828956267846360?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116828956267846360?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/wZxs84EmBNs/bad-internationalization.html" title="Rant: Bad Internationalization" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>7</thr:total><feedburner:origLink>http://www.xenomachina.com/2007/01/bad-internationalization.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYBRnw9cCp7ImA9WB5XEkw.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-2307216833384245665</id><published>2007-06-03T12:05:00.000-07:00</published><updated>2007-07-11T22:12:37.268-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-07-11T22:12:37.268-07:00</app:edited><title>Voodoo Electronics</title><content type="html">&lt;p&gt;I'm interested in robotics, but building robots requires three things: mechanics, electronics and software. (I'm intentionally ignoring BEAM here -- I'm not interested in building software-free robots.) I know software, and I feel like I have an okay grasp of mechanical systems. I don't know enough about electronics, though.

&lt;p&gt;My understanding of electronics is that there are basically three
types of electronics:

&lt;ul&gt;
  &lt;li&gt;AC electronics. Not especially relevant to building robots.
  &lt;li&gt;DC digital electronics. This part I feel like I pretty much understand. It's based on boolean logic, and so a lot of my software knowledge is applicable. The only extra bits are pretty simple:
    &lt;ul&gt;
      &lt;li&gt;you're dealing with high versus low rather than true/false, so you need to come up with a mapping convention for each circuit and stick with it.
      &lt;li&gt;many components (like ICs) also need power. Just get a power source that's the right voltage and enough current, and this should be okay.
      &lt;li&gt;there are a few different "families" of digital components. Stick with one for any one circuit.
    &lt;/ul&gt;
  &lt;li&gt;DC analog electronics. This has two main parts. The first part is the "simple bits": things like Ohm's law which &lt;em&gt;every single electronics tutorial&lt;/em&gt; feels the need to tell you about. The second part is what I've come to call "voodoo electronics" -- the parts that every electronics tutorial I've ever seen simply hand-waves away.
&lt;/ul&gt;

&lt;p&gt;A recent example of "voodoo electronics" that I encountered was in &lt;a href="http://makezine.com/10/"&gt;the most recent issue of Make&lt;/a&gt;. This issue had a number of electronics tutorials. Some of these were very helpful, like &lt;a href="http://makezine.com/10/workbench/"&gt;Your Electronics Workbench&lt;/a&gt;. The article on the 555 timer chip, however, &lt;a href="http://makezine.com/10/littlechip/"&gt;The Biggest Little Chip&lt;/a&gt;, suffered from the voodoo electronic problem. The article contained the following schematic:

&lt;p align=center&gt;&lt;img src="/images/555-voodoo-schematic.png" title="R3 = magic, C2 = more magic" alt='Figure 2 from "The Biggest Little Chip"' /&gt;&lt;/p&gt;

&lt;p&gt;It then went on to explain the purpose of each of the various components one by one. The explanations were a bit terse, but reasonable. Eventually, the entire circuit had been explained except for R3 and C2. Here's how the article explained their purpose:

&lt;blockquote&gt;
R3 protects the LED from excessive current, while C2 protects the 555 timer from random noise.
&lt;/blockquote&gt;

&lt;p&gt;"Excessive current"? "Random electronic noise"? How am I supposed to design my own circuits without understanding how to both predict when these problems will exist and how to devise a defense against them? So far I have yet to see a single electronics tutorial that explains these things.

&lt;p&gt;I ran into the same thing when I found some schematics for a stepper motor controller on the web. All of the schematics made sense to me, except a mysterious zener diode which was there (according to the documentation) "to absorb reverse EMF". No more explanation was given.

&lt;p&gt;Are there any electronics tutorials out there that actually explain how to predict when part of a circuit will be susceptible to "random electronic noise", "excessive current", or "reverse EMF" and how to build protection against them, or is my only hope to take a full-fledged course in electrical engineering?

&lt;h3&gt;Update&lt;/h3&gt;
&lt;p&gt;After posting this I realized that I could just post a comment on &lt;i&gt;Make&lt;/i&gt;'s website. The author of the 555 article posted a &lt;a href="http://makezine.com/10/littlechip/#msg2214"&gt;very helpful response&lt;/a&gt;. Now I just need to figure out what that zener diode in that stepper motor controller was for.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-2307216833384245665?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/lz7AJjIy0-M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/2307216833384245665/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=2307216833384245665" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/2307216833384245665?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/2307216833384245665?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/lz7AJjIy0-M/im-interested-in-robotics-but-building.html" title="Voodoo Electronics" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>8</thr:total><feedburner:origLink>http://www.xenomachina.com/2007/06/im-interested-in-robotics-but-building.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMARnY7fip7ImA9WBFbEEk.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-2237780704867869742</id><published>2007-05-01T11:10:00.000-07:00</published><updated>2007-05-01T11:17:27.806-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-05-01T11:17:27.806-07:00</app:edited><title>Copyright Law and the King</title><content type="html">&lt;p&gt;From the U.S. Copyright Office's &lt;a href="http://www.copyright.gov/help/faq/faq-protect.html"&gt;What Does Copyright Protect?&lt;/a&gt; page:
&lt;blockquote&gt;&lt;b&gt;How do I protect my sighting of Elvis?&lt;/b&gt;&lt;br&gt;
Copyright law does not protect sightings. However, copyright law will protect your photo (or other depiction) of your sighting of Elvis. Just send it to us with a Form VA application and the filing fee. No one can lawfully use your photo of your sighting, although someone else may file his own photo of his sighting. Copyright law protects the original photograph, not the subject of the photograph.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-2237780704867869742?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/ll9xmTJLwm4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/2237780704867869742/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=2237780704867869742" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/2237780704867869742?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/2237780704867869742?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/ll9xmTJLwm4/copyright-law-and-king.html" title="Copyright Law and the King" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.xenomachina.com/2007/05/copyright-law-and-king.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQARHw8cSp7ImA9WBFbFU0.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-1796802406259578145</id><published>2007-03-10T16:21:00.000-08:00</published><updated>2007-05-06T19:52:25.279-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-05-06T19:52:25.279-07:00</app:edited><title>Easier Links with Google AJAX Search API</title><content type="html">&lt;p&gt;Ever get annoyed by the complexity of adding links in blog or message board posts? Sure, it isn't rocket science, but between getting the angle brackets, quotes and start and end tags balanced correctly &lt;em&gt;and&lt;/em&gt; getting the URL right, adding links is a pain.

&lt;p&gt;This weekend I did some playing around with the &lt;a href="http://code.google.com/apis/ajaxsearch/"&gt;Google AJAX Search API&lt;/a&gt; and made a nifty bookmarklet for creating links in textareas. I've only tested it with Firefox (various combinations of 1.5 and 2.0 on Linux, Windows and Mac), and it only works with plain old multi-line textareas (not 1-line text fields or rich/"wysiwyg" text controls). Aside from those caveats, it seems to work pretty well.

&lt;p&gt;Installation is easy: just drag this link to your bookmark toolbar: &lt;a href="javascript:if%28window._XM_SEARCHPOP%29%7B_XM_SEARCHPOP.open%28%29%7Delse%7Bvar%20b%3Ddocument.body%3Bvoid%28z%3Ddocument.createElement%28%27script%27%29%29%3Bvoid%28z.src%3D%27http%3A//xenomachina.com/bookmarklets/searchpop.js%27%29%3Bvoid%28b.appendChild%28z%29%29%3B%7D"&gt;[linkify]&lt;/a&gt; Note that if you're reading this in a feed reader you'll probably need to visit my site for this step, as feed readers usually strip out JavaScript. (Readlings: no smirking.)

&lt;p&gt;Here's how to use it:

&lt;ol&gt;
&lt;li&gt;Go to some page that has a textarea. The comments area on most blogs should work. The Blogger posting form in "Edit Html" mode also works.
&lt;li&gt;Type some text in the textarea.
&lt;li&gt;Select the part you'd like to turn into a link.
&lt;li&gt;Activate the bookmarklet. This will bring up a search pane on the right side of your browser window.
&lt;p&gt;&lt;img alt="screen shot" src="http://xenomachina.com/images/searchpop-screenshot.png"&gt;
&lt;p&gt;The search query defaults to the text you'd highlighted. You can edit the query if necessary.
&lt;li&gt;Click on "create link" next to the search result. The popup will disappear, and the text you selected earlier (in the textarea) will be replaced with a link. The link's text will be the original selected text, and its href will be the URL of the search result you picked.
&lt;/ol&gt;

&lt;p&gt;Thanks to &lt;a href="http://www.massless.org/"&gt;Chris Wetherell&lt;/a&gt; for telling me about &lt;a href="http://tagneto.blogspot.com/2006/06/cross-domain-frame-communication-with.html"&gt;inter-frame communication with URL fragments&lt;/a&gt;, which turned out to be handy.

&lt;h3&gt;Update&lt;/h3&gt;
&lt;p&gt;I've been told that the bookmarklet works in Opera, but not in Safari. I'll look into the Safari issue.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-1796802406259578145?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/V2zShzUlLV4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/1796802406259578145/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=1796802406259578145" title="25 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/1796802406259578145?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/1796802406259578145?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/V2zShzUlLV4/easier-links-with-google-ajax-search.html" title="Easier Links with Google AJAX Search API" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>25</thr:total><feedburner:origLink>http://www.xenomachina.com/2007/03/easier-links-with-google-ajax-search.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQBQXw7eCp7ImA9WBBbFEQ.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-1511261393404092259</id><published>2007-01-10T13:59:00.001-08:00</published><updated>2007-01-10T22:19:10.200-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-01-10T22:19:10.200-08:00</app:edited><title>Intellectual Tug-O-War</title><content type="html">&lt;p&gt;Why is it that almost all of the online debate about intellectual
property is between extremists? On the one hand we have Richard
Stallman, Cory Doctorow and piracy apologists. On the
other hand we have the RIAA, MPAA and Microsoft/Apple
astroturfers. I tend to disagree with both of these groups because both
of them go too far -- just in opposite directions.

&lt;p&gt;The most recent example of this is the &lt;a href
="http://badvista.fsf.org/blog/analysis-of-microsofts-suicide-note-part-1"&gt;Analysis
of Microsoft's Suicide Note&lt;/a&gt; page. It's on fsf.org, so of course it's no
surprise that most of the comments are from free-software groupies. The only
exceptions are a few posts from "sreiser". I actually agree with some of
his points, but I strongly disagree with:7

&lt;blockquote&gt;by affording basic intellectual property protection within
the operating system, they [Microsoft] are doing great service towards
cultivating a more responsible public&lt;/blockquote&gt;

&lt;p&gt;The responses are, of course, equally extreme, but diametrically
opposite. For example:

&lt;blockquote&gt;...private piracy, on the other hand, does not cause a real
harm to the manufacturer, due to its nature. By private piracy I refer
to those people who download a certain product from Internet or
otherwise make unauthorized copies of it only for self use. This is, of
course, still illegal (although there are legal holes in many countries
than enable it), but even so it requires a closer look: most of 'private
pirates' (most probably more that 99%) copy the product just because
they cannot afford buying it. So, if you disable such user from doing
that, this won't make him/her to buy that product, since cannot afford
it. Instead that user won't use the product at all.&lt;/blockquote&gt;

&lt;p&gt;I don't agree with either of these quoted bits. On the one hand, if
I'm buying an operating system, that operating system should serve
&lt;em&gt;me&lt;/em&gt;, not the content providers. If there was somehow some sort
of DRM that didn't cause any harm, then I'd be okay with it, but not
causing any harm means:
&lt;ul&gt;
&lt;li&gt;It shouldn't make my machine slower.
&lt;li&gt;It shouldn't make my machine run hotter.
&lt;li&gt;It shouldn't increase the cost of the operating system or computer.
&lt;li&gt;It shouldn't increase the liklihood of bugs.
&lt;li&gt;It shouldn't make any legitimate tasks that I might want to
perform more difficult. This includes fair-use tasks like backing up my
media, moving it between my machines, burning it to a CD so I can listen
to it in my car, etc.
&lt;/ul&gt;Since no such DRM exists (or can exist, given the fuzziness of what is fair use) it's better to do without.

&lt;p&gt;On the other hand, the "private piracy doen't hurt anyone" argument
simply doesn't hold water.  There generally isn't a black and white
distinction between what one can afford and what one cannot. Can the
typical teenager afford to buy thousands of CDs? Probably not. Can they afford
to buy tens or even hundreds of CDs? For most American teenagers the
answer is probably yes. So is it okay for someone to buy as many CDs as
they can afford, and then to pirate the rest of their music?

&lt;p&gt;Even if you did consider such an absurd situation to be morally
justified, I argue that it would rarely happen. Once someone accepts
piracy as a legitimate option they'll pirate whenever the percieved
benefit minus cost outweighs that of purchasing. Given that many people's
wants expand to exceed their means, and that physical goods are
notoriously hard to pirate, people who have taken this path will not
purchase media even if they can afford it. Worse, this sort of behavior
can spread to those well outside of the "can't afford it" group because
it lowers the social stigma of pirating, while at the same time
decreasing the percieved value of the purchased product.

&lt;p&gt;One of the craziest things I've experienced when trying to talk with these extremeists is that they generally see me as being on the opposite side of the spectrum from them. The anti-copyright crowd, upon hearing that I think content creators should be able to use copyright in order to get compensation for their work will invariably accuse me of being against open source/"free" software. I actually like open source (I'm typing this on my Ubuntu box) and I've even contributed to some projects. I think of "open source" as a feature though, not a moral imperative. While I prefer my software to be open source, I don't consider the developers of non-open-source software to be infidels.

&lt;p&gt;Likewise, members of the the pro-DRM camp, upon hearing that I dislike DRM, will generally accuse me of being a software pirate. I just don't want fair use to be harmed. The "don't even talk about circumvention" bits in the DMCA are also frighteningly similar to &lt;a href="http://en.wikipedia.org/wiki/Thoughtcrime"&gt;thoughtcrime&lt;/a&gt;.

&lt;p&gt;Am I really the only one here in the middle? In the end, I'm inclined to side with the "information wants to be free" crowd, but mostly because they seem to be on the losing side of the tug-of-war.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-1511261393404092259?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/f0Lg3EH_NxQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/1511261393404092259/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=1511261393404092259" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/1511261393404092259?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/1511261393404092259?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/f0Lg3EH_NxQ/intellectual-property-extremists.html" title="Intellectual Tug-O-War" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>7</thr:total><feedburner:origLink>http://www.xenomachina.com/2007/01/intellectual-property-extremists.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUFSXs9fip7ImA9WBBWFkk.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-116564231950112007</id><published>2006-12-08T21:31:00.000-08:00</published><updated>2006-12-08T22:03:38.566-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2006-12-08T22:03:38.566-08:00</app:edited><title>Book: On The Edge</title><content type="html">&lt;a href="http://www.commodorebook.com/"&gt;&lt;img border=0 align=left alt=""
src="/images/on-the-edge-cover.jpg"&gt;&lt;/a&gt;&lt;p&gt;I recently finished reading
&lt;a href="http://www.commodorebook.com/"&gt;&lt;cite&gt;On the Edge: The
Spectacular Rise and Fall of Commodore&lt;/cite&gt;&lt;/a&gt;. I had a string of
Commodore computers from the time I was 8 up until I was in university,
so it was great to find out more about the history behind these
machines.

&lt;p&gt;One thing that comes to mind when I think about the Commodore
machines I had is how there was so little hardware abstraction. In
modern PCs you rarely talk directly to the hardware. Instead there are
high level languages and APIs piled on top of APIs. I'm not saying that
having little hardware abstraction was unique to the Commodore machines.
It was probably actually very common in other machines of that era as
well, since the machines were so underpowered (by today's standards)
that you couldn't really afford the extra overhead imposed by layers of
abstraction. However, as far as I can tell, the Commodore machines tended to 
have even less hardware
abstraction than their contemporaries.

&lt;p&gt;I think a large part of
this had to do with Commodore's habit of using custom chips in almost
every machine since the &lt;a
href="http://en.wikipedia.org/wiki/VIC-20"&gt;VIC-20&lt;/a&gt;, which was
actually named after the &lt;a
href="http://en.wikipedia.org/wiki/MOS_Technology_VIC"&gt;VIC&lt;/a&gt; chip the
machine was based around. In the &lt;a
href="http://en.wikipedia.org/wiki/Commodore_64"&gt;Commodore 64&lt;/a&gt; there
were the &lt;a href="http://en.wikipedia.org/wiki/VIC-II"&gt;VIC-II&lt;/a&gt; and &lt;a
href="http://en.wikipedia.org/wiki/6581"&gt;SID&lt;/a&gt; chips.  On the &lt;a
href="http://en.wikipedia.org/wiki/Amiga"&gt;Amiga&lt;/a&gt;, there was of course
the &lt;a href="http://en.wikipedia.org/wiki/Original_Amiga_chipset"&gt;famous
Amiga custom chipset&lt;/a&gt;.

&lt;p&gt;This hardware-centric philosophy permeated the system software as
well. I remember programming on the Commodore 64 in &lt;a
href="http://en.wikipedia.org/wiki/BASIC_2.0"&gt;BASIC 2.0&lt;/a&gt;, which
provided &lt;em&gt;absolutely no high-level support for sound or graphics
whatsoever&lt;/em&gt;. This was on the machine that arguably had some of the
best sound and graphics capabilities for its time. On the C64 everything
had to be done in &lt;a
href="http://en.wikipedia.org/wiki/PEEK_and_POKE"&gt;&lt;code&gt;PEEK&lt;/code&gt;s and
&lt;code&gt;POKE&lt;/code&gt;s&lt;/a&gt;. Even on the Amiga, which had a "real" operating
system, many hardware details would still peek (or is that poke?)
through, and applications frequently took advantage of hardware features
that were unique to the Amiga.

&lt;p&gt;From reading &lt;cite&gt;On the Edge&lt;/cite&gt; I think I have a better
understanding of why the Commodore machines were like this. A big factor
in this seems to be that the Commodore 64 and the Amiga development
teams included chip designers. The C64 pretty much started with the
VIC-II and SID chips, and the rest of the computer was built around
them.  This sort of makes sense, since Commodore was one of the only
computer makers of that era to have its &lt;a
href="http://en.wikipedia.org/wiki/MOS_Technology"&gt;own chip fab&lt;/a&gt;.
(According to the book, this was because &lt;a
href="http://en.wikipedia.org/wiki/Jack_Tramiel"&gt;Jack Tramiel&lt;/a&gt;,
Commodore's founder, was  obsessed with "vertical integration".)
Likewise, the Amiga's design centered around its own custom chips
largely because it was originally going to be a game machine. It
even sounds like one of the reasons Amiga was willing to be bought by
Commodore had to do with Commodore owning a chip fab.

&lt;p&gt;The book talks about both the engineering and the marketing behind
the Commodore machines. As a software engineer I'm obviously really
interested in the engineering stories, but some of the marketing stories
are also fascinating, especially given Commodore's notoriously bad
marketing in its later years.

&lt;p&gt;Here's a bit about Commodore's marketing tactics in 1983:

&lt;blockquote&gt;
Commodore instigated a strange offer that literally removed the
competition from homes. ... "Send in any sort of computing device and
you can get $100 off your Commodore 64." ...

&lt;p&gt;Commodore employees speculated that the offer actually increased
sales of &lt;a
href="http://en.wikipedia.org/wiki/Timex_Sinclair"&gt;Timex-Sinclair&lt;/a&gt;
computers. Customers sometimes purchased $50 Timex-Sinclair computers
just to take advantage of the $100 rebate offer. The excess Sinclair
computers became a running joke within the halls of Commodore. "We had
all these Sinclairs," says &lt;a
href="http://en.wikipedia.org/wiki/Bil_Herd"&gt;Bil Herd&lt;/a&gt;. "I started
using them as doorstops." &lt;/blockquote&gt;

What's especially interesting is how these stories can sometimes
intersect in surprising ways. During the development of the &lt;a
href="http://en.wikipedia.org/wiki/Commodore_128"&gt;Commodore 128&lt;/a&gt;
there was difficulty getting the C64's &lt;a
href="http://en.wikipedia.org/wiki/CP/M"&gt;CP/M&lt;/a&gt; and Magic Voice
cartridges to work on the C128 prototype:

&lt;blockquote&gt;
To fix the problem, Herd required the C128 to start at memory address
zero, but the 8502 started elsewhere. "One night, everybody left and it
was broken," says Herd. "During the night, I said, 'I have no way to fix
this, unless we startup by not starting at that address.' I said, 'Hey,
Von. The Z80 chip starts from zero, doesn't it?' He said, 'Yup.' I said,
'Cool. I need somebody wire wrapping tonight.'"

&lt;p&gt;The hour was too late to purchase a Z80 chip, so Herd looked
elsewhere. "Everybody had doorstops that were actually Sinclairs," he
recalls. "I went and tore open my doorstop because we didn't own a Z80
chip in the place."
&lt;/blockquote&gt;

&lt;p&gt;The Z80 in the C128 literally originated in a doorstop!

&lt;p&gt;If, like me, you grew up with Commodore computers, or if you're just
interested in the history of personal computers in general, then I
highly recommend reading
&lt;a href="http://www.commodorebook.com/"&gt;&lt;cite&gt;On the Edge&lt;/cite&gt;&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-116564231950112007?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/3_kkCo5nx94" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/116564231950112007/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=116564231950112007" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116564231950112007?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116564231950112007?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/3_kkCo5nx94/book-on-edge.html" title="Book: On The Edge" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://www.xenomachina.com/2006/12/book-on-edge.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMMSHs_fip7ImA9WBBQFUg.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-116355845344133949</id><published>2006-11-14T18:38:00.000-08:00</published><updated>2006-11-14T18:41:29.546-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2006-11-14T18:41:29.546-08:00</app:edited><title>Fubars for sale</title><content type="html">&lt;p&gt;I looks like &lt;a href="http://www.amazon.com/gp/product/B000FCGS0Y/ref=cm_gift_gg_B000FCGS0Y/002-2284732-5596012"&gt;fubars &lt;/a&gt; really exist. This review cracked me up:

&lt;blockquote&gt;... if all you want to do is knock down some walls or kill some enemies, this is the way to go.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-116355845344133949?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/lBpLbpRniho" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/116355845344133949/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=116355845344133949" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116355845344133949?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116355845344133949?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/lBpLbpRniho/fubars-for-sale.html" title="Fubars for sale" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://www.xenomachina.com/2006/11/fubars-for-sale.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EBQXYyfip7ImA9WBBQFk4.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-116297260408090845</id><published>2006-11-07T23:56:00.000-08:00</published><updated>2006-11-15T17:14:10.896-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2006-11-15T17:14:10.896-08:00</app:edited><title>Scala Programming Language</title><content type="html">&lt;p&gt;Today I went to &lt;a href="http://video.google.com/videoplay?docid=553859542692229789"&gt;a talk on the Scala
Programming Language&lt;/a&gt;. According to its creators, &lt;a href="http://scala.epfl.ch/"&gt;Scala&lt;/a&gt;
"smoothly integrates features of object-oriented and functional
languages". It's also got static type checking, and from the little bit
I've seen so far the type system seems fairly powerful. These are all
things I've wanted in a language for quite some time.

&lt;p&gt;The languages that I use at work, mostly Java and Python, are
normally classified as "object-oriented". Python does have a bit of a
functional flavor to it as well, but it seems that most people think of
functional and object-oriented styles of programming as almost
completely disjoint. I've been finding that I've been gradually moving
to an ever more "functional style" of object-oriented programming, even
when programming in Java, so I was happy to see a language designed to
integrate the two styles.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-116297260408090845?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/zXLi-Y6zeUM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/116297260408090845/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=116297260408090845" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116297260408090845?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/116297260408090845?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/zXLi-Y6zeUM/scala-programming-language.html" title="Scala Programming Language" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://www.xenomachina.com/2006/11/scala-programming-language.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8DQXY5fip7ImA9WBNVGUg.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-115695608006624271</id><published>2006-08-30T10:03:00.000-07:00</published><updated>2006-08-30T10:11:10.826-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2006-08-30T10:11:10.826-07:00</app:edited><title>Nerds versus geeks</title><content type="html">&lt;p&gt;Ask Yahoo recently had a post titled "&lt;a href="http://ask.yahoo.com/20060818.html"&gt;What's the difference between a nerd, a geek, and a dork?&lt;/a&gt;. Their definitions of nerd and geek are pretty consistent with the ones I've become used to since living in the SF Bay Area, but they aren't consistent with the way these terms were used when I was growing up.

&lt;p&gt;In my high school a geek was someone who was unpopular while a nerd was someone who was unpopular but smart. I remember when I first started reading &lt;a href="http://slashdot.org/"&gt;Slashdot&lt;/a&gt;, shortly after I moved to the Bay Area, I was really surprised at their use of the word "geek". "Geeks don't know how to use computers", I thought, "they're too busy &lt;a href="http://www.kli.org/"&gt;learning to speak Klingon&lt;/a&gt; or &lt;a href="http://www.cerado.com/web20quiz.htm"&gt;memorizing the names of Star Wars characters&lt;/a&gt;". Sure, there are  certain things that both geeks and nerds tend to be interested in, like science fiction, comic books and role playing games, but nerds are the ones who know how to do "useful" things. I became even more surprised when I learned that many slashdotters seemed to use an inverted set of definitions for geek and nerd.

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Nerd#Differences_from_geek"&gt;Wikipedia has a bit of an explanation&lt;/a&gt;:

&lt;blockquote&gt;Pundits and observers dispute the relationship of the terms "nerd" and "geek" to one another. Some view the geek as a less technically skilled nerd. Others view the exact opposite.&lt;/blockquote&gt;

&lt;p&gt;They also reference &lt;a href="http://www.catandgirl.com/view.php?loc=94"&gt;an excellent &lt;cite&gt;Cat and Girl&lt;/cite&gt;&lt;/a&gt; comic which, incidentally, defines nerd and geek in a way that's consistent with the definitions I grew up with.

&lt;p&gt;It sounds like the definitions of these two terms are regional. The Wikipedia page suggests that this may be an east-coast versus west-coast thing: on the east coast people think nerds are smart, while on the west coast people think geeks are smart.  That's certainly consistent with my experience, as I grew up in Ontario. It would be interesting if someone made a map-poll like the &lt;a href="http://www.popvssoda.com/"&gt;Pop vs. Soda Page&lt;/a&gt;. &lt;a href="http://www.lazyweb.org/"&gt;LazyWeb&lt;/a&gt;, don't fail me now!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-115695608006624271?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/Ds9ms_d08qU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/115695608006624271/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=115695608006624271" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/115695608006624271?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/115695608006624271?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/Ds9ms_d08qU/nerds-versus-geeks.html" title="Nerds versus geeks" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>5</thr:total><feedburner:origLink>http://www.xenomachina.com/2006/08/nerds-versus-geeks.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UHSX0yeyp7ImA9WBNWGEs.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-114843950255067405</id><published>2006-08-17T18:30:00.000-07:00</published><updated>2006-08-17T18:40:38.393-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2006-08-17T18:40:38.393-07:00</app:edited><title>Top 10 Java Classes I Love to Hate</title><content type="html">&lt;p&gt;I haven't posted here in months. What better way is there to end a blogging dry spell than a good rant?
&lt;p&gt;Here are ten Java classes in the standard API that annoy me whenever I have to deal with them, in no particular order:
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;java.io.File&lt;/code&gt;
&lt;dd&gt;An abstract file representation... or is it? It exposes system specific things like the &lt;code&gt;File.separator&lt;/code&gt; and &lt;code&gt;File.pathSeparator&lt;/code&gt;, yet it doesn't understand what "." and ".." do unless you canonicalize the &lt;code&gt;File&lt;/code&gt; object. It's also tied to the system's filesystem. This means you generally need to build an abstraction layer on top of &lt;code&gt;File&lt;/code&gt;. Finally, &lt;code&gt;File.lastModified()&lt;/code&gt; returns a &lt;code&gt;long&lt;/code&gt;. Why not a &lt;code&gt;Date&lt;/code&gt;? (&lt;code&gt;lastModified()&lt;/code&gt; used to be measured in arbitrary units from some arbitrary time. Not very useful if you want to be able to communicate the last modified time to the user or even another program. This was later fixed to be measured in milliseconds since the epoch.)
&lt;dt&gt;&lt;code&gt;java.io.Serializable&lt;/code&gt;
&lt;dd&gt;First, it's a marker interface. Marker interfaces are generally a bad smell. They're a good sign that someone was being lazy, or not thinking very carefully about the problem they were trying to solve. In the case of &lt;code&gt;Serialization&lt;/code&gt; it's especially bad because there are methods that probably should be in the interface: &lt;code&gt;writeObject()&lt;/code&gt;, &lt;code&gt;readObject()&lt;/code&gt; and &lt;code&gt;getSerialVersionUID&lt;/code&gt;. Instead, reflection is used to find methods that have "magic names". A definite no-no in my book.
&lt;p&gt;Of course, even that wouldn't fix the bigger issue which is that Java's serialization (like Python's pickling) is a very fragile mechanism for persisting objects which relies on the internal state of objects rather than on their public interfaces.
&lt;dt&gt;&lt;code&gt;java.lang.Cloneable&lt;/code&gt;
&lt;dd&gt;Like &lt;code&gt;Serializable&lt;/code&gt;, &lt;code&gt;Cloneable&lt;/code&gt; is a marker interface. It's even easier to see what's wrong with &lt;code&gt;Clonable&lt;/code&gt;, though. Where's the &lt;code&gt;clone()&lt;/code&gt; method? The docs mention its absence. That doesn't stop it from being a bug.

&lt;dt&gt;&lt;code&gt;java.text.MessageFormat&lt;/code&gt;
&lt;dd&gt;Overcomplicated. It defines a whole programming language for messages. This is great in theory, but it's not something you could actually give to translators. Hint: most translators are not computer programmers. Even if you manage to find a translator that can deal with the craziness of &lt;code&gt;MessageFormat&lt;/code&gt; chances are they don't know &lt;em&gt;every&lt;/em&gt; language you want to translate into.

&lt;dt&gt;&lt;code&gt;java.text.SimpleDateFormat&lt;/code&gt;
&lt;dd&gt;Almost every time I see this class used I see the same bug in the code that uses it. &lt;code&gt;SimpleDateFormat.format()&lt;/code&gt; is not reentrant and is not thread safe. Beyond that, &lt;code&gt;DateFormat&lt;/code&gt; has one of the most bizarre APIs ever. It has a &lt;code&gt;setCalendar&lt;/code&gt; method, but what does setting the "calendar" do? Why, it lets you get it back with &lt;code&gt;getCalendar&lt;/code&gt;! It also lets you stomp on some of format's internal state if you call it concurrently, but presumably it isn't meant for that.

&lt;dt&gt;&lt;code&gt;java.util.Calendar&lt;/code&gt;
&lt;dd&gt;What &lt;em&gt;isn't&lt;/em&gt; wrong with this class? Well, I guess it isn't a marker interface, at least.

&lt;p&gt;An instance of the &lt;code&gt;Calendar&lt;/code&gt; class represents what? The answer should be "a calendar", but in fact an instance of this class represents a &lt;em&gt;date&lt;/em&gt;. A mutable date whose mutators follow the rules of a particular calendar. Truly bizarre.

&lt;dt&gt;&lt;code&gt;java.util.Date&lt;/code&gt;
&lt;dd&gt;Date isn't so bad. It has two main sins: First, Date objects are mutable. Second, somehow it made someone feel the urge to write Calendar.

&lt;dt&gt;&lt;code&gt;java.util.Locale&lt;/code&gt;
&lt;dd&gt;Here's a &lt;a href="http://www.answers.com/locale"&gt;dictionary definition for "locale"&lt;/a&gt;:
&lt;blockquote&gt;
&lt;b&gt;lo·cale&lt;/b&gt; (lō-kăl')
&lt;br /&gt; &lt;i&gt;n.&lt;/i&gt;&lt;ol&gt;&lt;li&gt; A place, especially with reference to a particular event: &lt;i&gt;the locale of a crime.&lt;/i&gt;&lt;/li&gt;&lt;li&gt; The scene or setting, as of a novel.&lt;/li&gt;&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of the constants in &lt;code&gt;Locale&lt;/code&gt; fit this definition, and clearly represent "places":

&lt;pre class=code&gt;
static public final Locale CHINA = new Locale("zh","CN","");
static public final Locale FRANCE = new Locale("fr","FR","");
static public final Locale GERMANY = new Locale("de","DE","");
&lt;/pre&gt;

&lt;p&gt;Others, not so much:

&lt;pre class=code&gt;
static public final Locale CHINESE = new Locale("zh","","");
static public final Locale FRENCH = new Locale("fr","","");
static public final Locale GERMAN = new Locale("de","","");
&lt;/pre&gt;

&lt;p&gt;This is a pretty clear example of a common problem I've noticed with internationalization: a lot of people seem to confuse &lt;strong&gt;location&lt;/strong&gt; with &lt;strong&gt;language&lt;/strong&gt;. Is it &lt;em&gt;because of&lt;/em&gt; the Locale class, or is the &lt;code&gt;Locale&lt;/code&gt; class merely another victim of some sort of mass-hysteria? I don't know. In any case, the class probably should've been called &lt;em&gt;language&lt;/em&gt; since it's obviously based on &lt;a href="http://rfc.net/rfc1766.html"&gt;RFC1766: Tags for the Identification of &lt;strong&gt;Languages&lt;/strong&gt;&lt;/a&gt;.

&lt;dt&gt;&lt;code&gt;java.util.Stack&lt;/code&gt;
&lt;dd&gt;&lt;code&gt;Stack&lt;/code&gt; is mostly annoying because it used up a good name. I occasionally want a stack, but I never want a stack that extends &lt;code&gt;Vector&lt;/code&gt;. At least &lt;code&gt;Vector&lt;/code&gt; and &lt;code&gt;Hashtable&lt;/code&gt; had the decency to not use the only good names for what they do, but what else do you call a &lt;code&gt;Stack&lt;/code&gt;? A LIFO? Java 1.6 will actually add &lt;code&gt;Deque&lt;/code&gt; which can be used as a stack, but the interface is a lot "fatter" than than I'd like for the cases where I really just want a stack.

&lt;dt&gt;&lt;code&gt;java.util.WeakHashMap&lt;/code&gt;
&lt;dd&gt;Good idea, poor execution. First, the name is ambiguous. Weak what? Weak keys? Weak values? It turns out that it's weak keys, but it would be nice if the name said as much. It would also be nice if there was a weak value version so that people were more aware that they had to make a decision, as the two types of "weakness" are not interchangeable. You typically want weak keys when you're trying to "annotate" existing objects, but don't want the annotations to outlive the objects. You typically want weak values when you're trying to implement a weak cache keyed by something that's "recreatable" (typically "value objects", like Strings or numbers).

&lt;p&gt;Since it is a weak key hashmap it should actually be a weak key identity hashmap. It isn't, though. &lt;code&gt;WeakHashMap&lt;/code&gt; uses &lt;code&gt;Object.equals()&lt;/code&gt; despite the fact that weak references operate on identity.
&lt;/dl&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-114843950255067405?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/gZXWTodu7Ms" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/114843950255067405/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=114843950255067405" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/114843950255067405?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/114843950255067405?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/gZXWTodu7Ms/top-10-java-classes-i-love-to-hate.html" title="Top 10 Java Classes I Love to Hate" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.xenomachina.com/2006/08/top-10-java-classes-i-love-to-hate.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8DRH45eCp7ImA9WBJaGUU.&quot;"><id>tag:blogger.com,1999:blog-5051684.post-114909443561097552</id><published>2006-05-31T09:15:00.000-07:00</published><updated>2006-06-10T16:41:15.020-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2006-06-10T16:41:15.020-07:00</app:edited><title>Amazing Spaghetti</title><content type="html">&lt;a href="http://www.atariarchives.org/basicgames/"&gt;&lt;img border=0 align=right src="http://xenomachina.com/images/basic-computer-games-cover.gif" alt=""&gt;&lt;/a&gt;&lt;p&gt;On &lt;a href="http://www.gadgetopia.com/post/2386"&gt;Gadgetopia&lt;/a&gt; I read that "Someone scanned in all the pages from the classic book &lt;cite&gt;BASIC Computer Games&lt;/cite&gt;". I remember back in elementary school and high school I used to get magazines and books from the library with program listings.

&lt;p&gt;I rarely ever actually typed in the listings, though -- I was too lazy for that. Instead, I'd often try to figure out how the code worked. My favorites were the listings that also had an explanation of how the program worked. Then I'd typically ignore the listing entirely and just try to write a program based on the description. I remember writing a fractal landscape generator in BASIC 7.0 for the &lt;a href="http://en.wikipedia.org/wiki/Commodore_128"&gt;Commodore 128&lt;/a&gt; based on an article in &lt;a href="http://en.wikipedia.org/wiki/Compute_magazine"&gt;Compute!&lt;/a&gt; which had a listing for an IBM PC &lt;a href="http://en.wikipedia.org/wiki/Fractal_landscape"&gt;fractal landscape&lt;/a&gt; generator.&lt;a href="http://www.atarimagazines.com/compute/index/#issue85"&gt;&lt;img border=0 align=left src="http://xenomachina.com/images/compute-june1987-cover-small.jpg"&gt;&lt;/a&gt;

&lt;p&gt;Anyway, back to &lt;cite&gt;BASIC Computer Games&lt;/cite&gt;: Looking at the scanned cover, I felt like I remembered this book. I remember borrowing it from the public library back in high school and being surprised by the seemingly ancient version of BASIC that it was using. I don't know if it was actually an "ancient version of BASIC", that's just what it seemed like to me at the time. The book &lt;em&gt;was&lt;/em&gt; published in 1978, well before the &lt;a href="http://en.wikipedia.org/wiki/Commodore_64"&gt;Commodore 64&lt;/a&gt;, my first computer, even existed.

&lt;p&gt;There was one program in the book that really intriguied me: a random maze generator called "Amazing". By the time I got my hands on the book my programming language of choice was &lt;a href="http://en.wikipedia.org/wiki/Turbo_Pascal"&gt;Turbo Pascal&lt;/a&gt;, so I decided to try and port "Amazing". I never actually succeeded because Turbo Pascal is &lt;a href="http://en.wikipedia.org/wiki/Structured_programming"&gt;structured&lt;/a&gt;, while "Amazing" made copious use of spaghetti like &lt;code&gt;&lt;a href="http://en.wikipedia.org/wiki/GOTO"&gt;GOTO&lt;/a&gt;&lt;/code&gt;s.

&lt;p&gt;The first thing I did when I found out about the scan of the book today was to check if this really was the maze generator I remembered. I remembered that the program had lots of "&lt;code&gt;GOTO 1000&lt;/code&gt;" statements, and that line 1000 was itself a &lt;code&gt;GOTO&lt;/code&gt; statement (making the program worthy of &lt;a href="http://thedailywtf.com/"&gt;TheDailyWTF&lt;/a&gt;, IMHO). Sure enough, if you check &lt;a href="http://www.atariarchives.org/basicgames/showpage.php?page=3"&gt;the listing of Amazing&lt;/a&gt; you can see that spaghetti I was up against.

&lt;p&gt;The funny part is that the program listing is only about 150 lines long, which is actually pretty short. I think I'll take another crack at translating the program, though this time I'll probably use &lt;a href="http://python.org/"&gt;Python&lt;/a&gt; rather than Turbo Pascal.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5051684-114909443561097552?l=www.xenomachina.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/xenomachina/~4/O5Xk7zuKcC8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.xenomachina.com/feeds/114909443561097552/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5051684&amp;postID=114909443561097552" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/114909443561097552?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5051684/posts/default/114909443561097552?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/xenomachina/~3/O5Xk7zuKcC8/amazing-spaghetti.html" title="Amazing Spaghetti" /><author><name>Laurence</name><uri>http://www.blogger.com/profile/12946206226063265718</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="26" height="32" src="http://xenomachina.com/images/laurence.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.xenomachina.com/2006/05/amazing-spaghetti.html</feedburner:origLink></entry></feed>

