<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
        <title>Sean Kerwin dot org</title>
        <link>http://seankerwin.org/</link>
        <description>The Lex Luthor of Earth-Q</description>
        <language>en</language>
        <copyright>Copyright 2011</copyright>
        <lastBuildDate>Wed, 21 Dec 2011 16:05:00 -0500</lastBuildDate>
        <generator>http://www.sixapart.com/movabletype/</generator>
        <docs>http://www.rssboard.org/rss-specification</docs>
        
        <item>
            <title>Web Stylunk: Palette-Recoloring Sprites with HTML5</title>
            <description><![CDATA[ <p><b>Quick background info,</b> <i>or</i> <b>'what the hell is a Stylunk?'</b></p>

<p>I play <a href="http://clanlord.com">Clan Lord</a>, a multiplayer online RPG.  Clan Lord provides a lot of ways for players to customize their characters' appearance, including a system for changing shirt and pants colors with an algorithmic dye and bleach system.  Experimenting with dyes and bleaches in-game can be prohibitive &mdash; each application consumes in-game currency and (often rare) materials.  As a result, there's a long tradition of external dye-simulation tools; the earliest was Stylus (named in the 'function-us' pattern of many Clan Lord NPCs), followed by Stylos, the OS X re-imagining of Stylus.  Stylos didn't work well on Mac OS X 10.4 Tiger, so in 2005 I built the first version of Stylunk as a replacement.</p>

<p>Since then the number one Stylunk-related request has been for a Windows version, and the number two request is 'hey, add this new shirt color!'.  Both of which were difficult enough to fill that they happened never (Windows version) and only intermittently.  So I've decided to simplify cross-platform concerns and centralize updating by turning Stylunk into a web app.  The beta is <a href="http://seankerwin.org/stylunk-beta/">right here</a>.</p>

<p style="text-align:center;"><a href="http://seankerwin.org/stylunk-beta/"><img src="http://seankerwin.org/images/stylunk-screenshot.png" width="520" height="483" alt="Stylunk Screenshot" /></a></p>

<p><b>Palettized recoloring in JavaScript</b> <i>or</i> <b>'the actual non-background stuff about which I plan to write'</b></p>

<p>I started playing Clan Lord in 1998 and it had already been in development for a while at that point, so it shouldn't be a surprise that the tech isn't exactly modern.  Images in Clan Lord are an run-length-encoded list of indices into a per-image color map, which itself contains indices into the 'Mac standard' 256 color map.  That's all pretty integral to Clan Lord, because character customization is implemented as a set of 20 color overrides for the per-image colormap: indices zero and one contain the highlight and shadow colors for the character's hair, for instance.</p>

<p>Images for a particular icon are arranged into a single 16x3 sprite sheet, comprising the 32 standing/walking/running sprites in eight cardinal directions as well as a 'dead' sprite and fifteen 'poses' that players can activate to emote.  Here's what the 'Ghorak Zo' sprite sheet looks like:</p>
<p style="text-align:center;"><a href="http://seankerwin.org/images/zo-sprite-sheet.png"><img src="http://seankerwin.org/images/zo-sprite-sheet-shrunk.png" width="500" height="93" alt="Ghorak Zo Sprite Sheet" /></a></p>

<p>My first stab at implementing web Stylunk was about what you'd expect: I converted each of the sprite sheets into PNG files and wrote code that draws the selected sprite into a canvas, iterates across the pixels, and replaces the 'default' colors with the mapped values calculated from the current UI selection.  Then I created a smaller canvas, copied the frame I needed from the large canvas to the small canvas, and used <code>toDataURL()</code> to extract something that I could stick into the <code>background-image</code> of my buttons.</p>

<p>It all worked fairly well, at least at first.  On my main machine (a Core i7 Mac Mini) everything was peachy.  On my MacBook Air, it was sluggish.  On my iPad... well, the less said the better.</p>

<p>My immediate response was to try some very aggressive caching, leading to the creation of a <a href="http://seankerwin.org/archives/2011/12/a_simple_javascript_least-rece.shtml">reusable JavaScript least-recently-used cache</a>.  That helped, but not as much as I'd hoped.</p>

<p>My next thought was: I'm doing more work than I need to: there's no point colorizing the entire sprite sheet and then only displaying a single frame of it.  So I swapped things around; first extract a small canvas from the larger, <i>then</i> recolor only the smaller canvas.  That helped too, but it was still pretty logey on the iPad.<p>

<p>Unfortunately there's no way to profile JavaScript in MobileSafari.  But desktop Safari <i>does</i> have a profiler, and it showed something interesting: the slowest part of the process was reading the pixels out of the initial canvas.  So I took a cue from Clan Lord and took a step back in time: instead of storing the images as PNG sprite sheets, I wrote a tool that converts each frame of the sheet into a JavaScript data structure containing a run-length encoded bunch of pixel indices.  That way I can skip the 'reading from canvas' step, and jump straight to writing them.  I also took the step of storing each frame as a separate JavaScript file which the main app loads on-demand via <code>XMLHttpRequest</code>, which simplifies the caching a lot by letting the browser do the work.</p>

<p>If you're curious, you can take a look at the code for a sprite <a href="http://seankerwin.org/stylunk-beta/sprite-js-2/453/0_0.js">here</a>.  That's my JS-encoded version of the male Ghorak Zo facing east &mdash; the top-left sprite in the image above.  Yup! &mdash; despite being larger than a PNG representation or the same image, and despite needing to be parsed by the JavaScript engine, it <i>still</i> ends up being faster than reading data back from a canvas.</p>

<p>The end result is that Stylunk runs at a usable speed on my iPad (first-gen) and on my iPhone 4, which is good enough for me.  There are still some minor inefficiencies as a result of the tortured path to the current architecture &mdash; if I were doing this from scratch there would also be JavaScript representations of the character color maps, which would simplify the color mapping process and also allow me to easily draw characters using the color map data from the <a href="http://deltatao.com/clanlord/status/cldata.xml">XML data feed</a> powering <a href="http://deltatao.com/clanlord/status/">the Clan Lord Informer</a> and <a href="http://seankerwin.org/iclan/">iClan</a>.</p>

<p>I'm actually half-tempted to do that, because it would provide a starting point for building a <a href="http://socket.io/">Socket.IO</a>- and <a href="http://nodejs.org/">Node.js</a>-powered HTML5 Clan Lord client.  But that's a headache for another day.</p>]]></description>
            <link>http://seankerwin.org/archives/2011/12/web_stylunk_palette-recoloring.shtml</link>
            <guid>http://seankerwin.org/archives/2011/12/web_stylunk_palette-recoloring.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Clan Lord</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Javascript</category>
            
            
            <pubDate>Wed, 21 Dec 2011 16:05:00 -0500</pubDate>
        </item>
        
        <item>
            <title>A Simple JavaScript Least-Recently-Used Cache</title>
            <description><![CDATA[<p> In the course of a recent quick JavaScript side-project &mdash; post forthcoming &mdash; I needed a simple cache.  Then I needed another cache within the cached objects.  Then I needed another cache.  It was at that point that I realized I should probably factor things out into a reusable component, and built <a href="https://github.com/skirwan/JSLRU">JSLRU</a>.</p>

<p>Of course then I refactored things dramatically and it's barely used at all.  But if you need a JavaScript least-recently-used cache, <a href="https://github.com/skirwan/JSLRU">check it out</a>.</p>]]></description>
            <link>http://seankerwin.org/archives/2011/12/a_simple_javascript_least-rece.shtml</link>
            <guid>http://seankerwin.org/archives/2011/12/a_simple_javascript_least-rece.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Javascript</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Wed, 21 Dec 2011 14:30:38 -0500</pubDate>
        </item>
        
        <item>
            <title>Generic Accumulators in C#, Redux</title>
            <description><![CDATA[ <p>It turns out one of the most-trafficked pages on this site is <a href="http://seankerwin.org/archives/2008/12/generic_accumulators_in_c.shtml">my discussion of generic accumulators in C#</a>.  It occurs to me that it could use a bit of an update, as some newer features like lambdas and the predefined <code>Func&lt;&gt;</code> family simplifies things quite a bit:</p>

<code><pre>
class Program
{
    public static Func<T, T> MakeAccumulator<T>(T start, Func<T, T, T> addFunction)
    {
        return inc => start= addFunction(start, inc);
    }

    static void Main(string[] args)
    {
        var intAccumulator = MakeAccumulator(0, (i, j) => i + j);

        Debug.Assert(0 == intAccumulator(0));
        Debug.Assert(1 == intAccumulator(1));
        Debug.Assert(11 == intAccumulator(10));
        Debug.Assert(55 == intAccumulator(44));

        var floatAccumulator = MakeAccumulator(0.0, (i, j) => i + j);

        Debug.Assert(0 == floatAccumulator(0.0));
        Debug.Assert(0.1 == floatAccumulator(0.1));
        Debug.Assert(1.1 == floatAccumulator(1.0));
        Debug.Assert(5.5 == floatAccumulator(4.4));

        var stringAccumulator = MakeAccumulator("", (i, j) => i + j);

        Debug.Assert("" == stringAccumulator(""));
        Debug.Assert("ZYZ" == stringAccumulator("ZYZ"));
        Debug.Assert("ZYZZY" == stringAccumulator("ZY"));
        Debug.Assert("ZYZZYVA" == stringAccumulator("VA"));

        Console.WriteLine("Success!");
        Console.ReadLine();
    }
}
</pre></code>

<p>So there's that.  Still not terribly useful, but I <i>do</i> like shortening code.</p>]]></description>
            <link>http://seankerwin.org/archives/2011/12/generic_accumulators_in_c_redu.shtml</link>
            <guid>http://seankerwin.org/archives/2011/12/generic_accumulators_in_c_redu.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">.NET</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Wed, 21 Dec 2011 14:20:27 -0500</pubDate>
        </item>
        
        <item>
            <title>What Do You Call One Dead Terrorist?</title>
            <description><![CDATA[<p>&#8220;A good start.&#8221;</p>

 <p>Bad, old jokes aside:</p>

<p><a href="http://seankerwin.org/archives/2004/10/remember.shtml">I've said it before</a>, and I'll say it again: the Bush War on Terror was never about hunting down bin Laden.  To view it in that light is to characterize it as a campaign of retribution rather than one of pre-emptive self defense.  It is a simplistic and reductionist misrepresentation favored primarily by those who opposed Bush in every fashion, and it's a straw man that we would all do well to stop stuffing.</p>

<p>Some people make the world better through their existence, some make it worse, and the vast majority of us largely break even.  Osama bin Laden was clearly in the 'worse' column, and in that sense the world is a better place with him a corpse.  But to claim that his death in any measurable way makes us safer or surer or more just is folly.</p>]]></description>
            <link>http://seankerwin.org/archives/2011/05/what_do_you_call_one_dead_terr.shtml</link>
            <guid>http://seankerwin.org/archives/2011/05/what_do_you_call_one_dead_terr.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Politics</category>
            
            
            <pubDate>Mon, 02 May 2011 12:52:12 -0500</pubDate>
        </item>
        
        <item>
            <title>Should&apos;ve Bought an Xbox</title>
            <description><![CDATA[<p>I bought a Playstation 3 a few years back (when I got my HDTV, as I recall) primarily to use as a blu-ray player (at the time it was actually cheaper than a standalone player). As Sony doubtless expected, it entered my home as a shiny black Trojan horse, and I've bought a handful of games since then.</p>

<p>Including, fatefully <a href="http://us.playstation.com/flow/">one from the Playstation Network</a> (link goes to a Flash-only site, because Sony's just in full-on pissing-me-off mode at the moment).</p>

<p>If you're reading this there's roughly a 99% probability that you already know that the PSN has been 'out' for nearly a week, and the <a href="http://blog.us.playstation.com/2011/04/26/update-on-playstation-network-and-qriocity/">latest news from Sony</a> is that it ain't comin' back anytime soon, and oh, by the way, some hackers probably have your credit card numbers.</p>

<p>I know this the same way you do, through the magical collaborative power of the Internet.  Not, say, from an email sent to me by Sony warning me that my credit card number had been stolen.  Strike one, Sony.  You assholes.</p>

<p>But as annoying &mdash; and probably <i>actionable</i> &mdash; as their failure to proactively warn their customers may be, that's not what really pisses me off.</p>

<p>No, what's really bugging me is that I can't use Netflix on my PS3.</p>

<p>Why?  Because Netflix on the PS3 &mdash; despite being tied to my totally independent non-Sony Netflix account &mdash; requires a PSN login to function.  For no reason at all except that Sony are a bunch of goddamned morons.</p>

<p>Thank God for the <a href="http://www.amazon.com/gp/product/B001FA1NK0/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&tag=seankerorg-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=B001FA1NK0">Apple TV</a>.</p>]]></description>
            <link>http://seankerwin.org/archives/2011/04/shouldve_bought_an_xbox.shtml</link>
            <guid>http://seankerwin.org/archives/2011/04/shouldve_bought_an_xbox.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Technology</category>
            
            
            <pubDate>Tue, 26 Apr 2011 16:09:18 -0500</pubDate>
        </item>
        
        <item>
            <title>Change I Can Believe In</title>
            <description><![CDATA[<div style="text-align:center;"><img src="http://dl.dropbox.com/u/515062/nobama2012.png" /></div>]]></description>
            <link>http://seankerwin.org/archives/2011/04/change_i_can_believe_in.shtml</link>
            <guid>http://seankerwin.org/archives/2011/04/change_i_can_believe_in.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Humor</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Politics</category>
            
            
            <pubDate>Fri, 08 Apr 2011 23:34:49 -0500</pubDate>
        </item>
        
        <item>
            <title>I Still Hate Regions</title>
            <description><![CDATA[ <img src="http://seankerwin.org/images/MusicalRegions.png" alt="Sean hates regions" />
<br /><br />
(See also: <a href="http://seankerwin.org/archives/2009/05/your_code_sucks_please_stop_su.shtml"> Your Code Sucks, Please Stop Sucking</a>)]]></description>
            <link>http://seankerwin.org/archives/2010/12/i_still_hate_regions.shtml</link>
            <guid>http://seankerwin.org/archives/2010/12/i_still_hate_regions.shtml</guid>
            
            
            <pubDate>Wed, 01 Dec 2010 15:10:17 -0500</pubDate>
        </item>
        
        <item>
            <title>Potemkin Unit Testing</title>
            <description><![CDATA[<p>After the Russian Empire annexed the Crimean peninsula in 1783, Tsarina Catherine II (motto: &#8220;<a href="http://en.wikipedia.org/wiki/Tony_the_Tiger">I'm greeeeat!</a>&#8221;) took a tour of the area, guided by her trusted advisor and alleged-favored-non-equine-paramour Gregory Potempkin (or if you're not into the whole Anglicization thing, <i>Grigory Potyomkin</i>... or if you're <i>really</i> not into Anglicization, Григо́рий Алекса́ндрович Потёмкин).</p>

<p>As the story goes, Catherine's inspection took the form of a leisurely cruise down the Dniper River.  Potemkin, being aware that the Crimea was kind of a crummy area and fearful that the Tsarina would mislike the realization that her new territory kinda sucked, ordered the hasty construction of numerous fake village façades along the river bank.  The illusion held; Catherine went home thinking the Crimea was a bustling hub of non-squalid not-mud, and our modern world gained a vivid new idiom: 'Potempkin Villages'.</p>

<p>Of course all the actual evidence is that the story is bunk &#8212; Potemkin may have instructed the peasants to clean up a bit and maybe paint some fences, but the idea that the whole area was an elaborate set was an extreme exaggeration spread by his political rivals.  But it's one of those stories that's just too good to not tell, isn't it?  It's 'truthy'.</p>

<p>Changing direction entirely: <a href="http://en.wikipedia.org/wiki/Unit_testing">unit testing</a> is <a href="http://www.imdb.com/title/tt0120912/quotes">the new hotness</a> in software development.  The basic idea is that you decompose your software into small, manageable units and create dedicated automated tests that verify the functionality of those units.  And it is, in general, a really good idea.</p>

<p>The problem that crops up with unit tests is that some things are just really hard to test, particularly when your code interacts with something outside your control &#8212; such as a human user, a piece of hardware, or a third-party web service.  In those cases you need to get tricky; you create new layers of abstraction that separate the interaction from the main work of your module, and then you create 'mock' version of those abstraction layers and substitute in a 'simulated user' for a real user, or a 'simulated serial port' for your hardware, or a 'simulated web service' for the third-party service.</p>

<p>In some cases that approach may make sense.  For some pieces of hardware the range of possible states is limited; a button is up or down, and it's never both.  If a user is given an extremely limited set of buttons to click, you can reasonably simulate every possible interaction model.  But for anything more complex, you're kind of creating a Potemkin unit test.  So you've extracted out an <code>ITwitterApiProxy</code> that your <code>TwitterService</code> talks to, and you can plug in your <code>MockTwitterApiProxy</code> and write a whole host of tests for your <code>TwitterService</code>.  Every failure mode you can think of can be simulated by the <code>MockTwitterApiProxy</code> and you've coded the <code>TwitterService</code> to deal with all of them reasonably &#8212; and hurray, the unit tests mean you're safe!</p>

<p>Now you go to production, with the <code>RealTwitterApiProxy</code>, and stuff goes wrong.  Twitter's failing in ways you never anticipated!  So you add more switches and dials to your <code>MockTwitterApiProxy</code>, and add more code to your <code>TwitterService</code> to deal with them, and write tests to exercise them... and hurray, the unit tests mean you're safe!  ...at least until Twitter fails in another new way you haven't seen.</p>

<p>Compare that to how you would have handled things without unit tests: You code up <code>TwitterService</code>, handle the error cases you anticipate, go to production and discover some more error cases, update <code>TwitterService</code> to handle them... lather, rinse, repeat.  It's essentially the same workflow.</p>

<p>So what does the extra effort buy you?  What's the benefit of building your API abstraction and unit tests?</p>

<p>Well the obvious answer is that it buys you protection against regression.  You have an explicit test in place for handling error type A, so if in the course of implementing support for type G errors you accidentally break type A, the unit test will fail and you'll find out.  There's that, certainly.  Without unit tests it's basically down to trust: I trust myself (or my fellow developers) not to break <i>this</i> part of the code while working on <i>that</i> part of the code.</p>

<p>But on the other hand, your test for handling error type A isn't <i>really</i> testing your handling of error type A, it's testing your handling of your mock simulation of error type A.  How can you be sure that in the course of adding code to simulate error G to your mock you didn't accidentally break the code that simulates error type A?  Isn't that the same sort of "trust yourself" approach that the non-unit-testing approach is left to?  Should you add unit tests for your mocks to ensure that they're properly simulating errors?</p>

<p>Ultimately a lot of this is down to personal judgement, like most of the interesting questions in software development.  And I'm certainly not going to argue that unit tests aren't a good idea a lot of the time.  But I do believe that there are certain situations where unit tests create a lot of extra code to maintain without really adding much in the way of protection, and that committing to building such Potemkin unit tests is <a href="http://www.bartleby.com/100/420.47.html">a foolish consistency</a> of precisely the sort that will prove <a href="http://en.wikipedia.org/wiki/Hobgoblin_(comics)">a poor substitute for Norman Osborn</a>.</p>]]></description>
            <link>http://seankerwin.org/archives/2010/10/potemkin_unit_testing.shtml</link>
            <guid>http://seankerwin.org/archives/2010/10/potemkin_unit_testing.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Mon, 18 Oct 2010 12:53:42 -0500</pubDate>
        </item>
        
        <item>
            <title>Familia Corporātus</title>
            <description><![CDATA[<p>Is your company a company, or is your company a family?</p>

<p>The question has a number of rather pertinent ramifications.  It's good to have an answer.</p>

<p>If your company is a <i>family</i>, then when you invade my workspace for a family activity, preventing me from getting any work done and thereby causing me to miss deadlines and slip schedules, well, that's okay.  We're family.  We'll deal.  Pass the cake.</p>

<p>If, on the other hand, your company is a <i>company</i>, then when you invade my workspace for a family activity &mdash; preventing me from getting any work done and thereby causing me to miss deadlines and slip schedules &mdash; it's on <i>me</i> to make up the time.  Your actions have become <i>my</i> problems, which is ample justification for me to be more than a little peeved.</p>

<p>So is your company a company, or is your company a family?</p>

<p>If your company is like <i>most</i> companies, it's both &mdash; you get to do what you want, and I get screwed with no right to be angry about it.</p>

<p>Here's a protip about 'most companies' &mdash; people good enough to get jobs elsewhere... <i>do</i>.</p>]]></description>
            <link>http://seankerwin.org/archives/2010/04/familia_corporatus.shtml</link>
            <guid>http://seankerwin.org/archives/2010/04/familia_corporatus.shtml</guid>
            
            
            <pubDate>Tue, 20 Apr 2010 13:36:00 -0500</pubDate>
        </item>
        
        <item>
            <title>Damned Whippersnappers</title>
            <description><![CDATA[<p>Remember being a teenager*?</p>

<p>Teenagerititude is the state of believing that everyone else in the world is phenomenally stupid &mdash; that the solution to every problem is blatantly obvious, and that everyone would be much better off if they'd just shut up and do as you say.</p>

<p>The interesting thing about being a teenager is that you don't actually outgrow it.</p>

<p>At some point a light goes on in your mind, the scales drop from your eyes, the metaphor similes upon you, <i>whatever</i>, and you realize you've been a teenager and you stop.  You congratulate yourself on being so adult and on owning up to your past bad acts and you move on with your life.</p>

<p>And then a few years later you realize that you were mistaken, that you were actually still a very-slightly-more-evolved form of teenager despite that revelation, and the scales drop from  your lights and you eye what you metaphor and you move on with your life.</p>

<p>Until it happens <i>again</i>.</p>

<p>Eventually you reach a degree of meta-awareness &mdash; you recognize that Socrates kinda had a point about the whole 'knowing you know nothing' schtick.  That's when you ascend to a higher plane of existence!  Then you help Teal'c and MacGuyver out a few times, and eventually you return to the show with your tail between your legs because it turns out your landlord won't accept 'art' in lieu of money.  But hey, higher power.  You got <i>that</i> going for you.</p>

<p>Until, damn it, it happens <i>again</i>.</p>

<p>Meta ain't enough, nor is meta-meta.  Maybe there's some omega-meta state where you stop realizing that you're an idiot, and you get to draw a Batman logo on Anthro's cave wall just to reassure the idiots who didn't figure out the pattern based on Kal and Hal and whatnot.  But I can't be sure of that.  I think any form of personal growth boils down to suddenly recognizing what a jackass you've been and thus becoming an exciting <i>new</i> form of jackass.</p>

<p>...</p>

<p>So what's the moral of the story?  The moral is that I'm stupid, you're stupid, he's stupid, she's stupid, and the primary differentiating point between us is our awareness of our own incompetence.  So when something looks dumb to you, remember that it either <i>is</i> dumb, or it's smart in a way you haven't thought yet deciphered &mdash; and prudence dictates that you assume the latter until you've assembled reasonable support for the former.  And even if something turns out to be legitimately dumb, don't draw conclusions from that, because oftentimes there's more to the story.  It's not uncommon to find clever implementations of a piss-poor design; that may be a sign of abstractional schizophrenia, or it may be a sign of the blind leading the brilliant.</p>

<p><br />
*<i>If you currently are a teenager, shut up and get the hell off my lawn.  Damned kids.</i></p>]]></description>
            <link>http://seankerwin.org/archives/2010/03/damned_whippersnappers.shtml</link>
            <guid>http://seankerwin.org/archives/2010/03/damned_whippersnappers.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Humor</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Wed, 24 Mar 2010 14:27:56 -0500</pubDate>
        </item>
        
        <item>
            <title>The Largest Betamax Selection in Town!</title>
            <description><![CDATA[<p>Imagine that video rental shops worked like libraries; there's a whole network of affiliated branches, and if <i>this</i> library doesn't have it, they can get it at the cousin library across town and deliver it for you.  And as long as we're imagining, take it a step further: they can get it for you for no extra cost, absolutely <i>instantly</i>.</p>

<p>So that's the way video rentals work, and they all stock VHS cassettes, and that's that.  But now a sea change occurs: somebody invents DVDs.  All of the video rental shops start building their stock of DVD titles, and while doing so they take advantage of their affiliation network, so if your favored shop doesn't have <i>Joe Dirt</i> on DVD, they can get it from the shop across town.</p>

<p>But one little shop bucks the trend &mdash; even though DVD is the future, they decide to stick with VHS.  And that works out okay for them, because while the other rental shops are investing heavily in building a DVD library, they're making room by selling off their VHS cassettes, and this little dark-horse shop is gobbling them up like popcorn, and their library is <i>huge</i>.</p>

<p>Now it's ten years later.  Every video rental shop around the world has switched to DVD, but this little shop is still on VHS.  It's impossible to buy a VHS deck in stores, so this shop rents or sells you a VHS player when you sign up for membership, but that's okay actually &mdash; some people find that kind of convenient.</p>

<p>But now something interesting happens &mdash; a big, new, <i>hit</i> movie comes out... but only for DVD.  This little shop is caught-flat footed, because they simply can't compete here.  So what do they do?  They advertise!</p>

<p>This shop starts running ads, talking about how the local Blockbuster has a <i>tiny</i> library of titles, and it's absolutely terrible how small their library is.  Now, in a sense that's true; any individual Blockbuster may have a small library available, but keep in mind the affiliate network &mdash; when you get a DVD at Blockbuster, you may be getting it from any one of dozens of DVD rental places all over the world.</p>

<p>---</p>

<p>So what does any of that have to do with anything?</p>

<p>Well, VHS is the creaky old CDMA digital cellular technology.  DVDs are GSM, the modern cellular network architecture that almost every cellular carrier <i>around the world</i> uses.  Our hit DVD is obviously the iPhone.</p>

<p>And the tiny dark-horse video shop, that's Verizon.</p>

<p>Now, don't read this as a defense of AT&amp;T.  Personally I haven't had all that much trouble with them, but I know plenty of people who reflexively spit upon hearing those letters, and it's pretty clear that AT&T has been using their iPhone windfall to make <a href="http://www.penny-arcade.com/comic/2000/10/23/">money hats</a> rather than investing in their network infrastructure, and that's clearly subpar.  But while I dislike incompetence and don't care for spin, I absolutely <i>loathe</i> deception, and that's what Verizon's 'map' ads amount to, in my opinion.</p>

<p>The 'Verizon map' in those ads is, <i>by necessity</i>, a map of Verizon-owned-and-operated coverage zones; they <i>have</i> to be Verizon-owned-and-operated, because nobody else is running CDMA these days, just like nobody's using VHS or Windows 95.  The 'AT&amp;T map' to which they compare themselves, meanwhile, is of AT&amp;T-owned-and-operated coverage areas, which is not at all representative because AT&amp;T has reciprocal carriage agreements with GSM carriers throughout the US, and roaming agreements with GSM carriers <i>around the world</i>.</p>

<p>Personally, I can't stand Verizon &mdash; they have a policy of installing Verizon-controlled firmware on every phone they support, which tends to cripple the phone by removing built-in features to encourage you to use Verizon's expensive and inferior network-based equivalents.  Ever try to copy a ringtone to your Verizon phone using standard Bluetooth file transfer?  If you did, I'm betting it didn't go so well.  But Verizon does have a lot to offer customers, and if they want to be taken seriously they should compete by explaining the benefits of Verizon rather than deceiving people about the non-deficiencies of their competitors.</p>]]></description>
            <link>http://seankerwin.org/archives/2010/01/the_largest_betamax_selection.shtml</link>
            <guid>http://seankerwin.org/archives/2010/01/the_largest_betamax_selection.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Technology</category>
            
            
            <pubDate>Tue, 05 Jan 2010 17:42:47 -0500</pubDate>
        </item>
        
        <item>
            <title>Software Company Preventative Diagnostics</title>
            <description><![CDATA[<p>Ask each of your developers who the three best developers in the company are.  Only ask the developers &mdash; managers can't answer because they're in no good position to know.  Average out the votes and produce a consensus 'top three' list.  Hold on to it, and repeat the survey every six month.</p>

<p>If over a year has passed and two of those three people are still on the list, your company is probably screwed.  You might not actually have <i>realized</i> it yet, but that doesn't make it any less true.  Your management or business metrics may actually be positive, but if your development team isn't improving, the technology underlying everything is stagnating and sooner or later the smell will reach your customers.</p>

<p>Unless you already employ the best programers in the world, of course.</p>

<p>And if you really and truly believe that you employ the best programmers in the world, then unless your company name rhymes with 'Foogle', 'Schnapple', or 'Sicromoft', you're probably <i>really</i> screwed.</p>]]></description>
            <link>http://seankerwin.org/archives/2009/12/software_company_preventative.shtml</link>
            <guid>http://seankerwin.org/archives/2009/12/software_company_preventative.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Thu, 03 Dec 2009 16:13:14 -0500</pubDate>
        </item>
        
        <item>
            <title>Asynchronicity in C#: Error Handlers and Completion Calls</title>
            <description><![CDATA[<p>Normal function calls are easy to write; you call <code>DoSomething( )</code>, it executes and returns, and you continue on your merry way.</p>

<p>Asynchronous function calls seem, at first blush, only a little more difficult &mdash; instead of calling <code>DoSomething()</code> and executing your follow-up code after it returns, you pass in a callback: <code>BeginDoSomething( Action callback )</code>.</p>

<p>So, problem solved, let's go home.  Unless you need a return value, that is.  But even then it seems simple; to turn a synchronous method like <code>int CalculateSomething( )</code> into an asynchronous method, you just pass it a delegate that takes a parameter: <code>void BeginCalculateSomething( Action&lt;int&gt; callback )</code>.</p>

<p>So is that it?  Nope.  Because all of that is wrong.</p>

<p>Even though the original <code>DoSomething( )</code> method had no return type, it still had a return <i>path</i> &mdash; it could throw an exception.  Let's imagine that <code>BeginDoSomething</code> looks something like this:</p>

<pre><code>
public void BeginDoSomething(Action callback)
{
    PrepareForLongRunningOperation( );
    
    ThreadPool.QueueUserWorkItem(
        delegate
            {
                LongRunningOperation();

                callback();
            });
}
</code></pre>

<p>A handy way to think about this sort of thing is to figure out where a thrown exception would emerge.</p>

<p>If something goes wrong within the call to <code>PrepareForLongRunningOperation</code>, that happens in the same context as the calling code &mdash; any exceptions will throw up to the calling code and come out of its call to <code>BeginDoSomething</code>.  The same applies to the call to <code>ThreadPool.QueueUserWorkItem</code> &mdash; no problem there.</p>

<p>But what if <code>LongRunningOperation</code> throws?</p>

<p><code>LongRunningOperation</code> would throw up into whatever internal part of the ThreadPool implementation actually launched it.  That exception can't come out of <code>ThreadPool.QueueUserWorkItem</code>, because by the time the asynchronous anonymous delegate is running <code>ThreadPool.QueueUserWorkItem</code> has already returned.  And since the exception can't come out of <code>ThreadPool.QueueUserWorkItem</code>, it also can't come out of <code>BeginDoSomething</code> &mdash; which means there's no way for the calling code to get the exception.</p>

<p>There are two main approaches to this problem &mdash; error handlers and completion calls.</p>

<b>Error Handlers</b><br />

<p>Instead of passing your begin method one callback, pass two: a callback to be invoked if everything goes to plan, and an exception-accepting callback to which errors will be passed.</p>

<pre><code>
public void BeginDoSomething(Action callback, Action&lt;Exception&gt; errorHandler)
{
    PrepareForLongRunningOperation( );
    
    ThreadPool.QueueUserWorkItem(
        delegate
            {
                try
                {
                    LongRunningOperation();
                    callback();
                }
                catch(Exception ex)
                {
                    errorHandler(ex);
                }
            });
}

// Sample usage:

BeginDoSomething(
    delegate
    {
		// Do something now that we're done
    },
    delegate(Exception ex)
    {
		// Do something with the error
    });
</code></pre>

<p>There are strengths and weaknesses to this approach.  The biggest strength of this model is that it <i>forces</i> the calling code to think about error handling &mdash; the prompt for it is right there in the method signature.  Error handling tends to fall through the cracks in any sort of code, but it's especially easy to overlook in an asynchronous context (It's also a lot more dangerous in an asynchronous context, because often dropping a callback invocation will cause a process to spin forever, sucking down resources and accomplishing nothing).</p>

<p>Separating the success case from the failure case may be either a strength or a weakness, depending on the particular task.  Sometimes it makes your code much cleaner, but it often happens that your success and error handler need to share context and implementation, which can make for some very ugly code.</p>

<b>Completion Calls</b><br />

<p>Instead of just invoking a parameter-less <code>Action</code> callback or a single-parameter <code>Action&lt;TReturn&gt;</code> callback, your code calls a single-parameter callback and passes it an <code>Action</code> or <code>Func&lt;TReturn&gt;</code> that the callback in turn invokes.</p>

<pre><code>
public void BeginDoSomething(Action&lt;Action&gt; callback)
{
    PrepareForLongRunningOperation( );
    
    ThreadPool.QueueUserWorkItem(
        delegate
            {
                try
                {
                    LongRunningOperation();
                    callback(delegate { });
                }
                catch(Exception ex)
                {
                    callback(delegate { throw ex; });
                }
            });
}

// Sample Usage:

BeginDoSomething(
    delegate(Action complete)
    {
        try
        {
            complete();
        }
        catch (Exception ex)
        {
            // Do something with the error
            return;
        }
        // Do something now that we're done
	})
</code></pre>

<p>At first blush, this seems like a much clumsier solution; you're essentially trusting the calling code to call your completion method.  That's true, at least in this case.</p>

<p>Where completion calls really shine are for asynchronous calls returning values; instead of calling their callback and handing in an <code>Action</code>, you call their callback and give it a <code>Func&lt;TReturn&gt;</code>, which they then <i>must</i> invoke to get their result.  That gives you an opportunity to throw exceptions that they <i>can't</i> cleverly bypass:</p>

<pre><code>
public void BeginCalculateSomething(Action&lt;Func&lt;int&gt;&gt; callback)
{
    PrepareForLongRunningCalculation();

    ThreadPool.QueueUserWorkItem(
        delegate
        {
            try
            {
                int result = LongRunningCalculation();
                callback(() =&gt; result);
            }
            catch (Exception ex)
            {
                callback(delegate { throw ex; });
            }
        });
}

// Sample Usage:

BeginCalculateSomething(
    delegate(Func&lt;int&gt; complete)
    {
        int result;
        try
        {
            result = complete();
        }
        catch (Exception ex)
        {
            // Do something with the error
            return;
        }
        // Do something with the content of result;
	})
</code></pre>

<p>Personally I prefer completion calls, mainly because the the pattern works so well for return values.  In practice, any time you need this sort of a pattern it's because you care about return values; if you need to ensure that X happens after Y, it's generally because X depends on the result of Y.  If X <i>doesn't</i> depend on Y, that's often a sign that you're being too linear in your analysis and that the tasks should be happening in parallel.</p>

<p>Microsoft seems to have collectively reached the same conclusion; <code>IHttpAsyncHandler</code>, the asynchronous methods off of <code>SqlCommand</code>, and the asynchronous forms of <code>WebRequest</code> all use the completion call approach.</p>]]></description>
            <link>http://seankerwin.org/archives/2009/10/asynchronicity_in_c_error_hand.shtml</link>
            <guid>http://seankerwin.org/archives/2009/10/asynchronicity_in_c_error_hand.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">.NET</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Thu, 29 Oct 2009 13:27:42 -0500</pubDate>
        </item>
        
        <item>
            <title>Functional Programming</title>
            <description><![CDATA[<pre><code>public void BeginGetSingle(TIdentityCriteria identityCriteria, CompletionCallback&lt;TItem&gt; callback)
{
    TFilterCriteria filterCriteria =
        CriteriaUtilities.UpgradeCriteria&lt;TIdentityCriteria, TFilterCriteria&gt;(identityCriteria);

    RestClient.BeginGet&lt;TItem&gt;(
        CriteriaUtilities.CriteriaToUrl(
            filterCriteria,
            m_Map,
            m_ServiceUrlBase,
            r =&gt; typeof (TItem).FullName.Equals(r.OutputPayloadClass) &amp;&amp; r.AllowedVerbs.Contains(Verb.Get)),
        completionFunction =&gt; callback(() =&gt; completionFunction().Payload));        
}
</code></pre>

<p>My function takes a function and when done it calls that function, passing a function that the calling function calls for the result.</p>
<p>Now in fact my function calls another function taking a function accepting a function to call for its result, and to that function it passes a function which when called calls the first function passing a new function that when called calls the function that was passed to the function that my function passed to the other function, thereby returning the result of that function to the function that called my function.</p>
<p>And they say you can't write Lisp code in C#.</p> ]]></description>
            <link>http://seankerwin.org/archives/2009/10/functional_programming.shtml</link>
            <guid>http://seankerwin.org/archives/2009/10/functional_programming.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">.NET</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Tue, 27 Oct 2009 14:29:40 -0500</pubDate>
        </item>
        
        <item>
            <title>Xclan Night</title>
            <description><![CDATA[<p>Still a work in progress, but pretty cool looking already in my rarely-humble opinion.  The trick is to dim the red and green components while leaving the blue intact.</p>

<p><a href="http://dl.getdropbox.com/u/515062/xclan-night-9-4-09.png"><img src="http://dl.getdropbox.com/u/515062/xclan-night-9-4-09.png" alt="Xclan screenshot 1" style="width:100%" /></a><br />
<a href="http://dl.getdropbox.com/u/515062/xclan-night-9-4-09b.png"><img src="http://dl.getdropbox.com/u/515062/xclan-night-9-4-09b.png" alt="Xclan screenshot 2" style="width:100%" /></a></p>

<p>I'm trying to decide the best way to show text bubbles.  Everything's pretty scrupulously iPhone-friendly this time around (that's actually what got me started on Xclan again, figuring out how to work with UDP CFSockets in a run loop), but as I understand it uploading new text textures every few frames will bog an iPhone down pretty quickly.  I'm thinking of switching my NSOpenGLView to a CAOpenGLLayer and putting a bunch of CATextLayers on top for the bubbles.</p>

<p>There's actually an NSTextField in a layer in the game window; on Leopard it floats above the NSOpenGLView, but in Snow Leopard (where the screen shots were taken) it's invisible.  I haven't pursued it because I was planning on replacing the OGL view with a layer anyway, but it's a strange difference.</p>]]></description>
            <link>http://seankerwin.org/archives/2009/09/xclan_night.shtml</link>
            <guid>http://seankerwin.org/archives/2009/09/xclan_night.shtml</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Clan Lord</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Cocoa</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Programming</category>
            
            
            <pubDate>Fri, 04 Sep 2009 00:37:59 -0500</pubDate>
        </item>
        
    </channel>
</rss>

