<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-32241238</id><updated>2012-01-14T15:57:42.969-05:00</updated><category term='suggestions'/><category term='macheist'/><category term='SMART'/><category term='positive'/><category term='swing'/><category term='Xbox 360'/><category term='boost'/><category term='eBay'/><category term='safari extension'/><category term='C++'/><category term='encryption'/><category term='Arduino'/><category term='Warhammer 40k'/><category term='DES'/><category term='haskell'/><category term='hard disk'/><category term='windows'/><category term='repair'/><category term='SSL'/><category term='Apache'/><category term='Android'/><category term='Y combinator'/><category term='usability'/><category term='paint'/><category term='googlegears'/><category term='scala'/><category term='miniatures'/><category term='MacOS'/><category term='htcg1'/><category term='algorithm'/><category term='pointer'/><category term='Blogger'/><category term='Java'/><category term='electronics'/><category term='ui'/><category term='criticism'/><category term='iPhone'/><category term='Stack Overflow'/><category term='APM'/><category term='functional programming'/><category term='praise'/><category term='certificate'/><category term='VNC'/><category term='Gents with Beards'/><category term='TLS'/><category term='vista'/><category term='recursion'/><category term='T-Mobile'/><category term='Erlang'/><title type='text'>Why not?</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>77</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-32241238.post-7764667175232973751</id><published>2012-01-14T15:57:00.000-05:00</published><updated>2012-01-14T15:57:42.997-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><category scheme='http://www.blogger.com/atom/ns#' term='MacOS'/><title type='text'>Using Apache on Mac OS X to serve files outside ~/Sites</title><content type='html'>&lt;p&gt;I'm working on a web project that basically contains just static HTML and Javascript. (well OK, there's also one, small PHP script, but it might be going away in the near future). I tend to keep all my source code in ~/src, but to host it, I also need it to appear in ~/Sites. After some small trial-and-error, I ended up putting the everything (git repo and all) into ~/Sites, and then symlinked it to ~/src. It wasn't pretty, but it worked.&lt;/p&gt;&lt;p&gt;So I just did some reorganization that pretty much invalidated that old structure. In particular, I have moved everything that needs to be deployed into ~/src/project/web. However, I want it to be accessible via http://localhost/me/project. I tried physically moving the project back into ~/src, and then making a symlink to the subdirectory, but that didn't work. Apache would still produce 403s for all the relevant files. So I had to roll up my sleeves and dive into Apache configuration.&lt;/p&gt;&lt;p&gt;Before I go further, I'm compelled to pull out the old soapbox. I have painfully little experience with Apache - I have never had to configure or support it in a production environment, and that makes me happy. From this position of ignorance, I have decided that Apache is a dinosaur that should have died a long time ago. For example, instead of configuring the server from the request's point of view (as has been popularized by Rails' routing logic), it is configured from the filesystem's point of view. The default Mac OS configuration has, buried somewhere in the middle of the file, a directive that disallows the serving of all files under /. Because, I guess, they would be served by default if that directive wasn't present? But still, nobody seems to want to spend the time to produce a replacement web server, and so we struggle on. &amp;lt;/rant&amp;gt;&lt;/p&gt;&lt;p&gt;The default Apache install on MacOS 10.7 uses a split apache configuration file. The bulk of the configuration is in /etc/apache2/httpd.conf. However, each user also gets their own /etc/apache2/users/me.conf, which are all imported into the main configuration. And while the main configuration file specifies the FollowSymlinks option, I disconvered that the same is not true in my personal config file. All I had to do was to add the FollowSymlinks option to that configuration file, restart Apache, and everything started working.&lt;/p&gt;&lt;p&gt;So if you have only basic web serving needs, the default config should suffice. If, however, you want/need to spread the files around your disk, you need to mess with the Apache configuration.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-7764667175232973751?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/7764667175232973751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=7764667175232973751' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7764667175232973751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7764667175232973751'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2012/01/using-apache-on-mac-os-x-to-serve-files.html' title='Using Apache on Mac OS X to serve files outside ~/Sites'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6898861482577110147</id><published>2011-09-05T21:40:00.001-04:00</published><updated>2011-09-05T21:41:25.221-04:00</updated><title type='text'>Mysterious, Blank User in 10.7 Sharing Dialog</title><content type='html'>&lt;p&gt;I wanted to copy some files from my PC to my Mac. When I went to turn on SMB sharing, I came across this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://2.bp.blogspot.com/-uucQOhf-MVk/TmV2mSjhEBI/AAAAAAAAAS0/loCBoJPu7lE/s1600/Screen%2BShot%2B2011-09-05%2Bat%2B9.23.38%2BPM.png" imageanchor="1" style=""&gt;&lt;img border="0" height="339" width="400" src="http://2.bp.blogspot.com/-uucQOhf-MVk/TmV2mSjhEBI/AAAAAAAAAS0/loCBoJPu7lE/s400/Screen%2BShot%2B2011-09-05%2Bat%2B9.23.38%2BPM.png" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I was wondering about the identity of this phantom user. It turns out that it is the macports user. He doesn't show up on the login screen. He never showed up under 10.6. Apparently, Apple changed something about the way users are reported to applications.&lt;/p&gt;&lt;p&gt;If it bothers you, you can fix it with dscl.&lt;pre&gt;sudo dscl . -create /Users/macports RealName macports&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;The first parameter is the machine you want to administer; . is apparently a shortcut for localhost. Then we give the command - we want to create a new key. Then we specify where this key should be created - in this case, the macports user's Directory Services path. Next is the name of the key - RealName is what appears to be used by the sharing dialog. (RealName is also assigned on users you create through System Preferences). Finally, we provide a value for this user's name. Now, we have this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://2.bp.blogspot.com/-3ewJC1wHDAQ/TmV5eqq8bVI/AAAAAAAAAS8/flNJspcPw3g/s1600/Screen%2BShot%2B2011-09-05%2Bat%2B9.37.49%2BPM.png" imageanchor="1" style=""&gt;&lt;img border="0" height="339" width="400" src="http://2.bp.blogspot.com/-3ewJC1wHDAQ/TmV5eqq8bVI/AAAAAAAAAS8/flNJspcPw3g/s400/Screen%2BShot%2B2011-09-05%2Bat%2B9.37.49%2BPM.png" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6898861482577110147?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6898861482577110147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6898861482577110147' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6898861482577110147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6898861482577110147'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2011/09/mysterious-blank-user-in-107-sharing.html' title='Mysterious, Blank User in 10.7 Sharing Dialog'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-uucQOhf-MVk/TmV2mSjhEBI/AAAAAAAAAS0/loCBoJPu7lE/s72-c/Screen%2BShot%2B2011-09-05%2Bat%2B9.23.38%2BPM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4937250989459658343</id><published>2010-09-14T00:42:00.000-04:00</published><updated>2010-09-14T00:42:53.053-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VNC'/><category scheme='http://www.blogger.com/atom/ns#' term='DES'/><category scheme='http://www.blogger.com/atom/ns#' term='encryption'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>DES Encryption As Used in VNC Authentication</title><content type='html'>&lt;p&gt;A few notes about how DES is used in the VNC Authentication security type (type 2)&lt;/p&gt;&lt;ol&gt;&lt;li&gt;DES is used in ECB mode.&lt;/li&gt;&lt;li&gt;The ECB Key is based upon an ASCII password. The key must be 8 bytes long. The password is either truncated to 8 bytes, or else zeros are added to the end to bring it up to 8 bytes. &lt;a href="http://www.vidarholen.net/contents/junk/vnc.html" title="The DES encryption used by VNC servers"&gt;As an additional twist&lt;/a&gt;, each byte in flipped. So, if the ASCII password was "pword" &lt;code&gt;[0x 70 77 6F 72 64]&lt;/code&gt;, the resulting key would be &lt;code&gt;[0x 0E EE F6 4E 26 00 00 00]&lt;/code&gt;.&lt;/li&gt;&lt;li&gt; The VNC Authentication scheme sends a 16 byte challenge. This challenge should be encrypted with the key that was just described, but DES in ECB mode can only encrypt an 8 byte message. So, the challenge is split into two messages, encrypted separately, and then jammed back together.&lt;/li&gt;&lt;/ol&gt;Here is some pseudocode (in Erlang) that should explain better than words can.&lt;pre&gt;&lt;code class="language-erlang"&gt;password_to_key(Password) -&gt;&lt;br /&gt;    Flipped = lists:map(fun flip_byte/1, Password),&lt;br /&gt;    Truncated = truncate(Flipped, 8),&lt;br /&gt;    pad(Truncated, 8, $\0).&lt;br /&gt;&lt;br /&gt;encrypt_challenge(Password, Challenge) -&gt;&lt;br /&gt;    Key = password_to_key(Password),&lt;br /&gt;    &amp;lt;&amp;lt;High:8/binary, Low:8/binary&amp;gt;&amp;gt; = Challenge,&lt;br /&gt;    EncHigh = crypto:des_ecb_encrypt(Key, High),&lt;br /&gt;    EncLow = crypto:des_ecb_encrypt(Key, Low),&lt;br /&gt;    &amp;lt;&amp;lt;EncHigh/binary, EncLow/binary&amp;gt;&amp;gt;.&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4937250989459658343?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4937250989459658343/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4937250989459658343' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4937250989459658343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4937250989459658343'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2010/09/des-encryption-as-used-in-vnc.html' title='DES Encryption As Used in VNC Authentication'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-2835849280838741830</id><published>2010-07-28T22:03:00.001-04:00</published><updated>2010-07-28T22:04:25.118-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='safari extension'/><title type='text'>Unpacking a Safari Extension</title><content type='html'>&lt;p&gt;So, now that Safari extensions are official (and not just a developer curiosity), I decided to see what people had managed to make over at the &lt;a href="http://extensions.apple.com/" title="Apple - Safari - Safari Extensions Gallery"&gt;extension gallery&lt;/a&gt;. It looks like there are some cool ideas out there. I was somewhat interested in the &lt;a href="http://maypril.se/safariextensions/Exposer-1.1.safariextz" title="Exposer"&gt;Exposer&lt;/a&gt; extension, which sounded a bit like Exposé for Safari. It seems like it kinda works, except that it doesn't always bring up the list of windows, and it's also really slow (it looks like &lt;a href="http://developer.apple.com/safari/library/documentation/UserExperience/Reference/SBrowserTabClassReference/SafariBrowserTab/SafariBrowserTab.html#//apple_ref/javascript/instm/SafariBrowserTab/visibleContentsAsDataURL" title="SafariBrowserTab Class Reference"&gt;visibleContentsAsDataURL&lt;/a&gt; is the culprit, natch, plus I have dozens of tabs open at a time).&lt;/p&gt;&lt;p&gt;Anyway, while I was checking it out, I realized that I had no idea what some of these Safari extensions were doing in the background. Stop and think for a moment; do you really want to run code that some Jimmy wrote in his basement to be able to watch everything that you do in your browser? Maybe I'm just paranoid, but I'd like to know what is really going on.&lt;/p&gt;&lt;p&gt;So, naturally, I tried unpacking an extension. It wasn't particularly hard, but you have to realize that a .safariextz file isn't a ZIP archive. It's a XAR. I know; I opened it up in my &lt;a href="http://www.suavetech.com/0xed/0xed.html" title="0xED"&gt;hex editor&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Here's how you can unpack one for yourself:&lt;/p&gt;&lt;code class="command"&gt; xar -xvf &lt;span class="placeholder"&gt;extension&lt;/span&gt;.safariextz -C ~/Desktop&lt;/code&gt;&lt;p&gt;Don't worry; there's a directory just inside the safariextz archive. Now to see if there's anything malicious in these extensions. (Exposer looks clean so far.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-2835849280838741830?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/2835849280838741830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=2835849280838741830' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2835849280838741830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2835849280838741830'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2010/07/unpacking-safari-extension.html' title='Unpacking a Safari Extension'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-411834172724452742</id><published>2010-06-28T23:33:00.000-04:00</published><updated>2010-06-28T23:33:50.536-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miniatures'/><category scheme='http://www.blogger.com/atom/ns#' term='Warhammer 40k'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><title type='text'>What I learned today</title><content type='html'>&lt;p&gt;Drybrushing comes first.&lt;/p&gt;&lt;img src="http://3.bp.blogspot.com/_yWS30lH8nfY/TCloyQFCxDI/AAAAAAAAAQY/gYxXSH2Km7c/s1600/Metal.png" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-411834172724452742?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/411834172724452742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=411834172724452742' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/411834172724452742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/411834172724452742'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2010/06/what-i-learned-today.html' title='What I learned today'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_yWS30lH8nfY/TCloyQFCxDI/AAAAAAAAAQY/gYxXSH2Km7c/s72-c/Metal.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6387841677649383721</id><published>2010-04-29T19:48:00.000-04:00</published><updated>2010-04-29T19:48:32.682-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miniatures'/><category scheme='http://www.blogger.com/atom/ns#' term='Warhammer 40k'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><title type='text'>Even More Space Marines</title><content type='html'>&lt;p&gt;I've spent some more time on my Space Marine squad (it's going on something like 6 months at this point - I just don't get a ton of time to paint). Anyway, the tactical squad is basically done. Notice the highlights.&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh4.ggpht.com/_yWS30lH8nfY/S9oYmomHIiI/AAAAAAAAAP0/25Uvup_Vrlw/IMG_0628.jpg" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh6.ggpht.com/_yWS30lH8nfY/S9oYnvJ9AkI/AAAAAAAAAP8/KcjcWJ5k98U/IMG_0633.jpg" /&gt;&lt;/p&gt;&lt;p&gt;In addition, I started to work on some figures from &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat200190&amp;amp;prodId=prod1570027"&gt;Assault on Black Reach&lt;/a&gt;, including some terminators. In particular, I spent quite a while trying to get the white helmets to look good. I'm also pretty pleased with the eyes. No Golden Demons here, but not bad for tabletop.&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh3.ggpht.com/_yWS30lH8nfY/S9oYohtywMI/AAAAAAAAAQE/hZHHDOxTKvs/IMG_0663.jpg" /&gt;&lt;/p&gt;&lt;p&gt;Finally, I had a spare marine sitting around, so I decided to make him up as a Blood Angel. I have two copies of &lt;a href="http://www.boardgamegeek.com/boardgame/54625/space-hulk-third-edition"&gt;Space Hulk&lt;/a&gt; that I want to paint, but I wasn't going to do so until I was ready. I think I'm almost ready. Also, another shot of the terminators' eyes.&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh6.ggpht.com/_yWS30lH8nfY/S9oYqX6WT3I/AAAAAAAAAQM/rxkUVAQZlyc/IMG_0686.jpg" /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6387841677649383721?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6387841677649383721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6387841677649383721' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6387841677649383721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6387841677649383721'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2010/04/even-more-space-marines.html' title='Even More Space Marines'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_yWS30lH8nfY/S9oYmomHIiI/AAAAAAAAAP0/25Uvup_Vrlw/s72-c/IMG_0628.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-818745785828431674</id><published>2010-04-15T09:02:00.001-04:00</published><updated>2010-04-15T09:02:33.999-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSL'/><category scheme='http://www.blogger.com/atom/ns#' term='TLS'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='certificate'/><title type='text'>Things I Learned While Debugging an SSL Issue</title><content type='html'>&lt;ul&gt;&lt;li&gt;SSL is sometimes actually TLS. SSL is apparently on the way out, though TLS is only supported in a subset of common browsers. Fortunately, both use the same kind of certificate, so it's mostly transparent.&lt;/li&gt;&lt;li&gt;Java 1.6u17 removed SSL client support for MD2-signed root certificates. Except it sometimes didn't. Some u17 installs worked for me, some failed. 1.6u19 failed every time. If you have a Java client connecting to a SSL server, make sure that the server certificate was generated against a SHA1-signed root certificate.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.wireshark.org/" title="Wireshark &amp;middot; Go deep."&gt;WireShark&lt;/a&gt; will analyze both SSL and TLS. If there's any confusion about what is coming from the server, WireShark can help you figure it out.&lt;/li&gt;&lt;li&gt;The server sends the whole certificate chain to the client. I had thought that this was the case, but I had a hard time finding the documentation that spells it out. In the end, I used WireShark to find out.&lt;/li&gt;&lt;li&gt;Web browsers sometimes lie. When I would ask the web browser for the certificate chain, it would tell me something different from what the server actually sent. The root certificate from the server was signed with SHA1, but the browser would tell me that it was signed with MD2. This occurred in Internet Explorer, Firefox, and Safari. This was also a red herring that caused me to waste a lot of time.&lt;/li&gt;&lt;li&gt;Make sure you are looking at the right server. I had made an assumption about how the Java client software talked to the server, and that assumption was incorrect. In the end, the problematic certificate was on a different server altogether. Go figure.&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/32241238-818745785828431674?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/818745785828431674/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=818745785828431674' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/818745785828431674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/818745785828431674'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2010/04/things-i-learned-while-debugging-ssl.html' title='Things I Learned While Debugging an SSL Issue'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4628155432521619906</id><published>2010-01-29T12:53:00.000-05:00</published><updated>2010-01-29T12:53:50.583-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='recursion'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='Y combinator'/><title type='text'>Deriving the Y Combinator in Erlang - Part 2: Abstraction</title><content type='html'>&lt;p&gt;This is the second post in a series on the Y combinator. &lt;a href="http://bytecrafter.blogspot.com/2010/01/deriving-y-combinator-in-erlang-or.html" title="Why not?: Deriving the Y Combinator in Erlang (or Recursive, Anonymous Functions for the Masses)"&gt;Part 1&lt;/a&gt;&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;In the &lt;a href="http://bytecrafter.blogspot.com/2010/01/deriving-y-combinator-in-erlang-or.html" title="Why not?: Deriving the Y Combinator in Erlang (or Recursive, Anonymous Functions for the Masses)"&gt;last post&lt;/a&gt; on the Y combinator, we established that some functional languages (such as Erlang) make it hard to have recursive, anonymous functions. Namely, there is no name that is bound to the "current" anonymous function. Furthermore, we established that we could work around this problem by passing the anonymous function to itself. When we concluded, we had derived this much:&lt;pre class="code"&gt;&lt;br /&gt;Foo = fun(AlsoFoo, N) -&amp;gt;&lt;br /&gt;    case N of&lt;br /&gt;        1 -&amp;gt; 1;&lt;br /&gt;        _ -&amp;gt; N * AlsoFoo(AlsoFoo, N - 1)&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt; Foo(Foo, X) end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;This post will focus on extracting the Y combinator from the above code. We will follow a sequence of transformations, each with a specific intent. In the end, we will hopefully have some beautiful code.&lt;/p&gt;&lt;h4&gt;Reducing the Number of Arguments&lt;/h4&gt;&lt;p&gt;This factorial algorithm started with a function that took a single parameter. This function called itself with successively smaller values, until it reached a base case. However, we muddied the waters by passing the function around as well. We would like to return to having a one-parameter function. We can accomplish this by creating another level of closure:&lt;pre class="code"&gt;&lt;br /&gt;Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * (AlsoFoo(AlsoFoo))(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt; (Foo(Foo))(X) end&lt;br /&gt;&lt;/pre&gt;Now it is clear that our recursive function is really only calling itself with one parameter.&lt;/p&gt;&lt;h4&gt;Simplifying the Recursive Function Call&lt;/h4&gt;&lt;p&gt;The place where we make the recursive call is rather ugly. We have to build the function upon which we will recurse before we can actually call it. It would be better if the recursive function was explicitly named. We can do that, too:&lt;pre class="code"&gt;&lt;br /&gt;Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; &lt;br /&gt;                AlsoFooAlsoFoo = fun(Z) -&amp;gt; (AlsoFoo(AlsoFoo))(Z) end,&lt;br /&gt;                N * AlsoFooAlsoFoo(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt; (Foo(Foo))(X) end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;We can simplify the body further by moving &lt;code&gt;AlsoFooAlsoFoo&lt;/code&gt; to a higher scope.&lt;pre class="code"&gt;&lt;br /&gt;Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;    AlsoFooAlsoFoo = fun(Z) -&amp;gt; (AlsoFoo(AlsoFoo))(Z) end,&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * AlsoFooAlsoFoo(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt; (Foo(Foo))(X) end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;As an aside, you might find this step overly complicated. You may ask, why did we not simply define &lt;code&gt;AlsoFooAlsoFoo = AlsoFoo(AlsoFoo)&lt;/code&gt;. I hope to get into the details in a later post, but for now, just realize that would change the evaluation order in a bad way. Or, try it yourself and find out why it doesn't work.&lt;/p&gt;&lt;h4&gt;Extracting the Anonymous Function&lt;/h4&gt;&lt;p&gt;Right now, the body of our algorithm is embedded deeply within some necessary plumbing. We would like to extract our algorithm from the center of this. This is quite easy:&lt;pre class="code"&gt;&lt;br /&gt;Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;    AlsoFooAlsoFoo = fun(Z) -&amp;gt; (AlsoFoo(AlsoFoo))(Z) end,&lt;br /&gt;    FactRec = fun(Self) -&amp;gt;&lt;br /&gt;        fun(N) -&amp;gt;&lt;br /&gt;            case N of&lt;br /&gt;                1 -&amp;gt; 1;&lt;br /&gt;                _ -&amp;gt; N * Self(N - 1)&lt;br /&gt;            end&lt;br /&gt;        end&lt;br /&gt;    end,&lt;br /&gt;    FactRec(AlsoFooAlsoFoo)&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt; (Foo(Foo))(X) end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Now we can pull it outside the body of &lt;code&gt;Foo&lt;/code&gt;.&lt;pre class="code"&gt;&lt;br /&gt;FactRec = fun(Self) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * Self(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;    AlsoFooAlsoFoo = fun(Z) -&amp;gt; (AlsoFoo(AlsoFoo))(Z) end,&lt;br /&gt;    FactRec(AlsoFooAlsoFoo)&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt; (Foo(Foo))(X) end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;h4&gt;Simplifying Fact&lt;/h4&gt;&lt;p&gt;We're getting close, but the definition of &lt;code&gt;Fact&lt;/code&gt; still leaves something to be desired. However, in order to make it simpler, we have to first make it messier. Start by moving &lt;code&gt;Foo&lt;/code&gt; inside the definition for &lt;code&gt;Fact&lt;/code&gt;:&lt;pre class="code"&gt;&lt;br /&gt;FactRec = fun(Self) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * Self(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt;&lt;br /&gt;    Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;        AlsoFooAlsoFoo = fun(Z) -&amp;gt; (AlsoFoo(AlsoFoo))(Z) end,&lt;br /&gt;        FactRec(AlsoFooAlsoFoo)&lt;br /&gt;    end,&lt;br /&gt;    &lt;br /&gt;    (Foo(Foo))(X)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Our goal is to build a general-purpose function &lt;code&gt;Y&lt;/code&gt; that takes a function &lt;code&gt;F&lt;/code&gt; and produces a self-recursive version of that function. Right now, the innermost part of &lt;code&gt;Foo&lt;/code&gt; makes an explicit reference to &lt;code&gt;FactRec&lt;/code&gt;. We want to eliminate that explicit reference:&lt;pre class="code"&gt;&lt;br /&gt;FactRec = fun(Self) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * Self(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt;&lt;br /&gt;    Y = fun(Proc) -&amp;gt;&lt;br /&gt;        Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;            AlsoFooAlsoFoo = fun(Z) -&amp;gt; (AlsoFoo(AlsoFoo))(Z) end,&lt;br /&gt;            Proc(AlsoFooAlsoFoo)&lt;br /&gt;        end,&lt;br /&gt;        Foo(Foo)&lt;br /&gt;    end,&lt;br /&gt;    (Y(FactRec))(X)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Now that we've done this, &lt;code&gt;Y&lt;/code&gt; no longer has any bound variables, so we can pull it out of &lt;code&gt;Fact&lt;/code&gt; completely:&lt;pre class="code"&gt;&lt;br /&gt;FactRec = fun(Self) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * Self(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Y = fun(Proc) -&amp;gt;&lt;br /&gt;    Foo = fun(AlsoFoo) -&amp;gt;&lt;br /&gt;        AlsoFooAlsoFoo = fun(Z) -&amp;gt; (AlsoFoo(AlsoFoo))(Z) end,&lt;br /&gt;        Proc(AlsoFooAlsoFoo)&lt;br /&gt;    end,&lt;br /&gt;    Foo(Foo)&lt;br /&gt;end,&lt;br /&gt;&lt;br /&gt;Fact = fun(X) -&amp;gt;&lt;br /&gt;    (Y(FactRec))(X)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Of course, if we want, we can simplify some of these definitions. &lt;code&gt;Y&lt;/code&gt; can become a normal Erlang module function (rather than a function value). &lt;code&gt;Fact&lt;/code&gt; itself can be curried - we can eliminate the noise of the explicit parameter. Also, &lt;code&gt;FactRec&lt;/code&gt; doesn't need to be named anymore - it can become the anonymous function that we originally intended:&lt;pre class="code"&gt;&lt;br /&gt;y(F) -&amp;gt;&lt;br /&gt;    G = fun(AlsoG) -&amp;gt;&lt;br /&gt;        F(fun(Z) -&amp;gt; (AlsoG(AlsoG))(Z) end)&lt;br /&gt;    end,&lt;br /&gt;    G(G).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Fact = y(fun(Self) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * Self(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end)&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Unfortunately, the &lt;code&gt;y&lt;/code&gt; function only supports functions that take a single parameter. Some languages have a "splat" operator that can be used to represent "all the parameters;" unfortunately, Erlang does not. Instead, it can be useful to define a family of y functions that deal with functions taking more than one parameter:&lt;pre class="code"&gt;&lt;br /&gt;y2(F) -&amp;gt;&lt;br /&gt;    G = fun(AlsoG) -&amp;gt;&lt;br /&gt;        F(fun(Y, Z) -&amp;gt; (AlsoG(AlsoG))(Y, Z) end)&lt;br /&gt;    end,&lt;br /&gt;    G(G).&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;We have shown the difficulty in defining recursive, anonymous functions in Erlang. We showed a simple solution to this problem, and then generalized the plumbing to make it easier to use. While this is not necessary in all functional languages, I hope that this is useful to anybody working in a strict language, such as Erlang.&lt;/p&gt;&lt;p&gt;I am planning more posts on this topic. One post will explain the strange step I took in Simplifying the Recursive Function Call. Others will explain just what a fixed point is, what the fixed point combinators are, and how the Y combinator satisfies the definition of a fixed point combinator.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4628155432521619906?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4628155432521619906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4628155432521619906' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4628155432521619906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4628155432521619906'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2010/01/deriving-y-combinator-in-erlang-part-2.html' title='Deriving the Y Combinator in Erlang - Part 2: Abstraction'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8638143881113854608</id><published>2010-01-18T14:18:00.004-05:00</published><updated>2010-01-29T12:59:39.069-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='recursion'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='Y combinator'/><title type='text'>Deriving the Y Combinator in Erlang - Part 1: Intuition</title><content type='html'>&lt;p&gt;This is the first of a series on the Y combinator. &lt;a href="http://bytecrafter.blogspot.com/2010/01/deriving-y-combinator-in-erlang-part-2.html" title="Why not?: Deriving the Y Combinator in Erlang - Part 2: Abstraction"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;When I heard about the &lt;a href="http://en.wikipedia.org/wiki/Fixed_point_combinator" title="Fixed point combinator - Wikipedia, the free encyclopedia"&gt;fixed point combinators&lt;/a&gt; before, I didn't know what to make of them, so I filed the topic away in my brain. However, when I was working on implementing continuations in Erlang, I ended up building a small structure that reminded me of the Y combinator. With a little massaging, I extracted the actual Y combinator, and proceeded with what I was working on.&lt;/p&gt;&lt;p&gt;The actual definition of the Y combinator is insanely dense:&lt;blockquote&gt;&lt;code&gt;Y = λf·(λx·f (x x)) (λx·f (x x))&lt;/code&gt;&lt;/blockquote&gt;This is precisely why I didn't understand it at first - that definition means nothing to me. We need a more intuitive way to think about it.&lt;/p&gt;&lt;p&gt;Suppose you decide to write the factorial function in Erlang. A simple (by which I mean unoptimized) implementation might look like this:&lt;pre class="code"&gt;&lt;br /&gt;fact(N) -&amp;gt; &lt;br /&gt;    case N of&lt;br /&gt;        1 -&amp;gt; 1;&lt;br /&gt;        _ -&amp;gt; N * fact(N - 1)&lt;br /&gt;    end.&lt;br /&gt;&lt;/pre&gt;There's nothing particularly complicated here - we're just calling &lt;code&gt;fact&lt;/code&gt; recursively. But what happens if you try to make &lt;code&gt;fact&lt;/code&gt; into a fun (an anonymous function in Erlang). Watch:&lt;pre class="code"&gt;&lt;br /&gt;Fact = fun(N) -&amp;gt; &lt;br /&gt;    case N of&lt;br /&gt;        1 -&amp;gt; 1;&lt;br /&gt;        _ -&amp;gt; N * ??? (N - 1) %%How do we call ourselves? Fact isn't yet bound!&lt;br /&gt;    end&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;In some languages, we could replace the &lt;code&gt;???&lt;/code&gt; with &lt;code&gt;Fact&lt;/code&gt;. Unfortunately, Erlang doesn't let you do this. If you tried, Erlang would say that &lt;code&gt;Fact&lt;/code&gt; is unbound. This is true - until we've finished defining the fun, we can't assign it to &lt;code&gt;Fact&lt;/code&gt;. Other languages provide you with a magic variable that represents the current function (Javascript has &lt;code&gt;arguments.callee&lt;/code&gt;). Again, as far as I know, Erlang doesn't provide such a variable. Does that mean that we have no hope?&lt;/p&gt;&lt;p&gt;Let's look at this problem one step at a time. We need something to stand in for the &lt;code&gt;???&lt;/code&gt;. We need a name that represents the current, anonymous function. Where can we get that from? In functional Erlang, there are only three ways that names are bound - by closure, by parameter, or by local definition. We can't close over it, because the anonymous function isn't yet defined. We can't create a local definition, because the local scope is too narrow a scope for that. That leaves only one possibility - we need to pass the anonymous function to itself.&lt;p&gt;&lt;p&gt;&lt;pre class-"code"&gt;&lt;br /&gt;Foo = fun(AlsoFoo, N) -&amp;gt;&lt;br /&gt;    case N of&lt;br /&gt;        1 -&amp;gt; 1;&lt;br /&gt;        _ -&amp;gt; N * AlsoFoo(AlsoFoo, N - 1)&lt;br /&gt;    end&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;Fact = fun(N) -&amp;gt; Foo(Foo, N) end.&lt;br /&gt;&lt;/pre&gt;OK, so we created a helper function - more on that in a minute. &lt;code&gt;Foo&lt;/code&gt; (formerly &lt;code&gt;Fact&lt;/code&gt;) now takes an extra parameter, which just creates another name for the current, anonymous function. Since we intend for that to be the same as &lt;code&gt;Foo&lt;/code&gt;, we call it &lt;code&gt;AlsoFoo&lt;/code&gt;. We know that &lt;code&gt;Foo&lt;/code&gt; is a fun/2. Since &lt;code&gt;AlsoFoo&lt;/code&gt; is supposed to be another name for &lt;code&gt;Foo&lt;/code&gt;, then &lt;code&gt;AlsoFoo&lt;/code&gt; must be a fun/2 as well. This means that, when we call &lt;code&gt;AlsoFoo&lt;/code&gt;, we need to also tell it about itself - that is, we need to pass &lt;code&gt;AlsoFoo&lt;/code&gt; along when we call &lt;code&gt;AlsoFoo&lt;/code&gt; to recurse.&lt;/p&gt;&lt;p&gt;Now that leaves us to deal with the function &lt;code&gt;Fact&lt;/code&gt;. Clearly, &lt;code&gt;Fact&lt;/code&gt; needs to call &lt;code&gt;Foo&lt;/code&gt;. We noted that &lt;code&gt;Foo&lt;/code&gt; is a fun/2, so again, we need to call it with two parameters. The intent of the extra parameter to &lt;code&gt;Foo&lt;/code&gt; was to be able to pass &lt;code&gt;Foo&lt;/code&gt; to itself, so we do just that.&lt;/p&gt;&lt;p&gt;Believe it or not, we have just derived the concept behind the Y combinator. We have invented a scheme that allows an anonymous function to know itself, even in a language that doesn't directly support this. This is (I believe) the purpose behind the Y combinator. However, we're not yet done. There is still some cruft that we would like to eliminate. In particular, we hand-built the plumbing to route &lt;code&gt;AlsoFoo&lt;/code&gt; around. We would like to use higher order functions to eliminate this. This is what the Y combinator does - it manages the plumbing of anonymous functions that refer to themselves.&lt;/p&gt;&lt;p&gt;In the &lt;a href="http://bytecrafter.blogspot.com/2010/01/deriving-y-combinator-in-erlang-part-2.html" title="Why not?: Deriving the Y Combinator in Erlang - Part 2: Abstraction"&gt;next part&lt;/a&gt;, we will continue the derivation of the Y combinator in Erlang. Our goal is to eventually be able to write something like this:&lt;pre class="code"&gt;&lt;br /&gt;Fact = y(fun(Self) -&amp;gt;&lt;br /&gt;    fun(N) -&amp;gt;&lt;br /&gt;        case N of&lt;br /&gt;            1 -&amp;gt; 1;&lt;br /&gt;            _ -&amp;gt; N * Self(N - 1)&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end).&lt;br /&gt;&lt;/pre&gt;It's not perfect, but in a language that doesn't directly support anonymous function recursion, it's not too bad!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8638143881113854608?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8638143881113854608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8638143881113854608' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8638143881113854608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8638143881113854608'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2010/01/deriving-y-combinator-in-erlang-or.html' title='Deriving the Y Combinator in Erlang - Part 1: Intuition'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3737403038949618922</id><published>2009-12-07T14:52:00.000-05:00</published><updated>2009-12-07T14:52:05.428-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miniatures'/><category scheme='http://www.blogger.com/atom/ns#' term='Warhammer 40k'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><title type='text'>Even More Space Marine Painting</title><content type='html'>&lt;p&gt;I've been slowly working on my Space Marines. It's taken a while, but they almost look like a unit. I think I've spent between 10 and 20 hours on them, but much of that was spent learning. Most of the major painting is done, and now it's time for touchups and details. For example, I spent some time on that rocket launcher to make it appear to be metal, painted red, and then worn. I think it's pretty convincing. I intend to do the same with that red bolter. The one that's not wearing armor needs a lot of work. Enjoy.&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh6.ggpht.com/_yWS30lH8nfY/Sx1adoTKHkI/AAAAAAAAAMM/CLDG3mADl4g/Overview.JPG" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh6.ggpht.com/_yWS30lH8nfY/Sx1afX9Pt3I/AAAAAAAAAMU/c3pT4H_KLaQ/Rocket.JPG" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh4.ggpht.com/_yWS30lH8nfY/Sx1agWCV2uI/AAAAAAAAAMc/Wyo0tAZu5IA/Bolter.JPG" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh5.ggpht.com/_yWS30lH8nfY/Sx1agzxMpcI/AAAAAAAAAMk/8mnygdawJv0/Flame.JPG" /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3737403038949618922?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3737403038949618922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3737403038949618922' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3737403038949618922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3737403038949618922'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/12/even-more-space-marine-painting.html' title='Even More Space Marine Painting'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_yWS30lH8nfY/Sx1adoTKHkI/AAAAAAAAAMM/CLDG3mADl4g/s72-c/Overview.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3892267348197212583</id><published>2009-11-17T18:08:00.000-05:00</published><updated>2009-11-17T18:08:10.014-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='boost'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='pointer'/><title type='text'>Standard algorithms and boost::ptr_vector</title><content type='html'>&lt;p&gt;I did something bad the other day.&lt;/p&gt;&lt;p&gt;OK, I can't tell if it was bad. In another environment, it would have been bad, but since this was C++, perhaps it was OK. I was in the situation where I had a &lt;a href="http://www.boost.org/doc/libs/1_40_0/libs/ptr_container/doc/ptr_vector.html" title="Boost Pointer Container Library"&gt;boost::ptr_vector&lt;/a&gt;, and I wanted to use a standard algorithm on it. Specifically, I wanted to use &lt;a href="http://www.cplusplus.com/reference/algorithm/partition/" title="partition - C++ Reference"&gt;std::partition&lt;/a&gt; to separate the objects that were still "alive" from those that were "dead" (where alive and dead are domain concepts in our application). The complexity here is that ptr_vector is a crazy container.&lt;/p&gt;&lt;p&gt;Most containers deal with a specific type T. You add Ts to the container. Dereferencing an iterator gives you a T&amp;amp;. It's generally assumed that a container operates on a single type, and the standard algorithms make this assumption.&lt;/p&gt;&lt;p&gt;The ptr_vector, on the other hand, appears to be two containers at once. Semantically, it's analogous to a std::vector&amp;lt;managed_ptr_type&amp;lt;T&amp;gt; &amp;gt;. It is intended that, by adding a pointer to the ptr_vector, the ptr_vector takes ownership of the lifetime of the memory at the end of the pointer. So, it is a container of pointers. On the other hand, when iterating a ptr_vector, it appears that it is a container of Ts.&lt;/p&gt;&lt;p&gt;In my case, I wanted to rearrange my ptr_vector. In particular, I wanted to partition the pointers into those whose object was still "alive", and those whose object was "dead". Since a ptr_vector is semantically a container of pointers, it made sense that I should apply std::partition to the ptr_vector. However, ptr_vector::iterator removes a level of indirection: instead of iterating T*, it iterates T&amp;amp;.&lt;/p&gt;&lt;p&gt;In fact, ptr_vector doesn't seem to provide any ways to rearrange the pointers once they are put into the container. Sure, you can mutate the object on the end of the pointer. You could operate at that level. But there doesn't appear to be a safe way to treat the ptr_vector as a container of pointers.&lt;/p&gt;&lt;p&gt;Fortunately, ptr_vector provides a back door. Its iterators support a base() method, which will return an iterator over T* (instead of an iterator over T&amp;amp;). This allows us to treat the ptr_vector as a container of pointers, and to use standard algorithms to manipulate those pointers. Now, this is not without peril. While it seems to be OK to rearrange the pointers, it wouldn't be safe to change the set of pointers. I wouldn't trust using something like &lt;a href="http://www.cplusplus.com/reference/algorithm/remove_if/" title="remove_if - C++ Reference"&gt;std::remove_if&lt;/a&gt;, because it might leave garbage in the container after it is done. The container might contain duplicate pointers. Some pointers might get dropped completely. If the container then goes out of scope, it will try to delete these pointers multiple times, which would be a bad thing. It might also fail to delete some pointers, because they were overwritten (and not preserved elsewhere in the container).&lt;/p&gt;&lt;p&gt;This whole thing felt like the best solution possible, while at the same time leaving a lot to be desired. I felt like I was violating the encapsulation of the ptr_vector. I suppose this is one of those cases for which they put in the base() methods on the iterators. Additionally, I don't see any clear way that they could do better. For example, I think an assumption of ptr_vector is that a given pointer only occurs inside it at most once. The standard algorithms don't necessarily respect this assumption; see my commentary on remove_if in the previous paragraph. The standard algorithms, in some cases, expect more freedom than ptr_vector can provide. This disconnect is unfortunate, but not without reason.&lt;/p&gt;&lt;p&gt;An important first step to helping with this problem would be to add methods to ptr_vector (and its siblings) that allow you to treat it as a container of pointers. You could add, remove, and re-arrange the container using these methods. In addition, they could maybe provide specializations of some of the standard algorithms for each container. This is difficult for third party developers to do, since the actual type of a ptr_vector::iterator is implementation defined. The boost guys can cleanly provide a specialization of std::partition for this kind of iterator, but I can't. Now, this isn't perfect. It would help with the standard algorithms, but not third-party algorithms. Still, it would be a great step in the right direction.&lt;/p&gt;&lt;p&gt;So, did I do something bad, or did I do something necessary?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3892267348197212583?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3892267348197212583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3892267348197212583' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3892267348197212583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3892267348197212583'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/11/standard-algorithms-and-boostptrvector.html' title='Standard algorithms and boost::ptr_vector'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-270817180102326957</id><published>2009-11-02T12:49:00.002-05:00</published><updated>2009-11-02T12:49:47.479-05:00</updated><title type='text'>Why Google Experience phones are pretty awesome</title><content type='html'>&lt;p&gt;As Android has grown, devices fall into one of two major classifications. Some devices are so-called "Google Experience" devices (featuring the phrase "with Google" somewhere on the device). Other devices are, well, not Google Experience devices. What is the difference? I've had a hard time figuring it out.&lt;/p&gt;&lt;p&gt;I think that Google Experience phones are updated by Google itself, while the rest of the devices are supported by the phone's manufacturer. I have an original G1 (a Google Experience phone), and I've gotten prompt updates as each new Android OS version has been released. This is similar to the experience that iPhone users enjoy.&lt;/p&gt;&lt;p&gt;Some devices, such as the HTC Hero and the Motorola Cliq (and the HTC Magic in certain regions), are not Google Experience phones. These phones were released with heavily customized software (such as HTC's Sense UI or Motorola's Motoblur). These customizations, while attractive to some users, also make it much harder for the phone manufacturer to update to a new version of the base Android OS. Both the Hero and the Cliq shipped with Android 1.5, and I don't believe that there are announced plans to update either to 1.6 (or 2.0, for that matter).&lt;/p&gt;&lt;p&gt;At first, I thought that the notion of a Google Experience phone was silly. At the time, the Magic was launching on Rogers with Exchange support, and that somehow disqualified the phone as being a Google Experience device. I now understand that Google Experience really means "unforked code base". In order to add Exchange support, I suspect that HTC had to fork and modify the standard Mail app. While they were able to add a feature that people wanted, it really just makes these phones into some sort of mutant Android device. No thank you. Google should really make it clear to users that the Google Experience is a feature in and of itself.&lt;/p&gt;&lt;p&gt;Android, at this point, is a rapidly evolving platform. Google Experience phones seem to be the best way to keep up with this evolution. I was pleased when I heard a Verizon rep say that the Droid will be a Google Experience phone. Now they just need to release a T-Mobile US GSM version, and I'll be happy. Over time, Android evolution will slow down, and then it might make sense for a manufacturer to fork the Android code base. Maybe they would even be willing to contribute back to the core distribution. But, until then, I'm sticking with Google Experience devices.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-270817180102326957?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/270817180102326957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=270817180102326957' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/270817180102326957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/270817180102326957'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/11/why-google-experience-phones-are-pretty.html' title='Why Google Experience phones are pretty awesome'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1992130746802689436</id><published>2009-11-02T12:13:00.000-05:00</published><updated>2009-11-02T12:13:41.838-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hard disk'/><category scheme='http://www.blogger.com/atom/ns#' term='APM'/><category scheme='http://www.blogger.com/atom/ns#' term='MacOS'/><category scheme='http://www.blogger.com/atom/ns#' term='SMART'/><title type='text'>Fixing hard disk clicking / aggressive head parking on Mac OS X</title><content type='html'>&lt;p&gt;I recently bought a Western Digital Scorpio notebook hard drive to put into my 2007-vintage Macbook Pro. Everything seemed fine at first. However, as I used my laptop, I noticed that it would frequently make a quiet clicking noise. At first, I thought that I had gotten a bad disk. However, after doing a little research, it became clear that this is a common problem. This clicking is a "normal" operational noise - it is the sound of the heads parking.&lt;/p&gt;&lt;p&gt;People say that you should just get used to the noise. However, &lt;a href="http://dougitdesign.com/blogs/blog_12_08_mac-clicking-hdd-hard-drive-noise-hdapm.html" title="Does your Apple notebook hard drive (HDD) ever sound like little mice are playing table tennis inside of it? Or, why your HDD might be pre-programmed for quick failure."&gt;this blog post&lt;/a&gt; makes the argument that every one of these clicks is killing your hard disk. Some people claim that this is related to the sudden motion sensor that's built into most (if not all) Apple portables. However, this is a red herring. The disk still clicks even if it is sitting on a table. It is the hard disk's own built-in power management that is causing the head parking. The disk's SMART statistics record the number of head parking cycles. If you want to see this for yourself, you can use either &lt;a href="http://sixtyfive.xmghosting.com/products/smartctl/" title="Sixty Five, Ltd. &amp;raquo; smartctl"&gt;this menu extra&lt;/a&gt; or &lt;a href="http://sourceforge.net/apps/trac/smartmontools/wiki" title="smartmontools"&gt;this command line tool&lt;/a&gt; (&lt;a href="http://smartmontools.darwinports.com/" title="Smartmontools version 5.38 - How to Download and Install on Mac OS X"&gt;MacPorts&lt;/a&gt;). You are looking for the Load Cycle Count value.&lt;/p&gt;&lt;p&gt;To explain the problem (as I understand it), modern hard disks have some responsibility to manage their power consumption. One manifestation of this is to spin down the platters and to park the read/write heads. The operating system can influence the time before the heads are parked by setting the "APM Level" of the drive to a value between 0x00 and 0xfe. Each drive manufacturer is free to interpret this value as they see fit. Mac OS X seems to set a default APM Level for all disks, and I think this value is 0x80. This is fine with Apple-shipped disks, but not necessarily for third party disks.&lt;/p&gt;&lt;p&gt;But wait! Perhaps you have bought the same kind of drive that Apple ships in their laptops. Are you safe? Not necessarily. Allegedly, Apple flashes their own firmware onto the the hard disks that they install at the factory. That's right, you're not running stock disk firmware. My suspicion is that this firmware changes the drive's interpretation of the default APM level. Recently, there was &lt;a href="http://www.apple.com/downloads/macosx/apple/firmware_hardware/harddrivefirmwareupdate20.html" title="Apple - Downloads - Firmware &amp;amp; Hardware - Hard Drive Firmware Update 2.0"&gt;a firmware update&lt;/a&gt; from Apple that fixed this problem on disks that were shipped by Apple. Unfortunately, you can't use this utility to flash the new firmware onto a non-Apple drive.&lt;/p&gt;&lt;p&gt;Right, so the two solutions that I see are either:&lt;ol&gt;&lt;li&gt;Write our own firmware&lt;/li&gt;&lt;li&gt;Set a different APM Level value&lt;/li&gt;&lt;/ol&gt;Obviously, option 2 looks much more attractive. Bryce McKinlay wrote a utility called &lt;a href="http://mckinlay.net.nz/hdapm/" title="hdapm Download page"&gt;hdapm&lt;/a&gt; for doing just that. He even includes a launchd configuration to run hdapm as the system starts. One thing not mentioned in the readme is that you need to get the permissions of the launchd config file correct. The file needs to be owned by root (preferably root:wheel), and must not be group- or world-writeable. I also changed the config file a little; here is my version:&lt;/p&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;&lt;br /&gt;&amp;lt;plist version="1.0"&amp;gt;&lt;br /&gt;&amp;lt;dict&amp;gt;&lt;br /&gt; &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;&lt;br /&gt; &amp;lt;string&amp;gt;hdapm&amp;lt;/string&amp;gt;&lt;br /&gt; &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;&lt;br /&gt; &amp;lt;array&amp;gt;&lt;br /&gt;  &amp;lt;string&amp;gt;/usr/local/bin/hdapm&amp;lt;/string&amp;gt;&lt;br /&gt;  &amp;lt;string&amp;gt;disk0&amp;lt;/string&amp;gt;&lt;br /&gt;  &amp;lt;string&amp;gt;max&amp;lt;/string&amp;gt;&lt;br /&gt; &amp;lt;/array&amp;gt;&lt;br /&gt; &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;&lt;br /&gt; &amp;lt;true/&amp;gt;&lt;br /&gt;&amp;lt;/dict&amp;gt;&lt;br /&gt;&amp;lt;/plist&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The biggest change is that I removed the "LaunchOnlyOnce" and "ServiceDescription" keys. I didn't see a reason to load it only once, and ServiceDescription seemed undocumented. This solution isn't perfect, however. First of all, hdapm uses a seemingly undocumented back door to adjust the APM setting. Ideally, we would actually spawn a daemon that continuously monitors and adjusts the drive's APM level. I'm not yet convinced that Mac OS X won't override my setting. Still, I have been running with this configuration for a couple of days, and things seem to be working well.&lt;/p&gt;&lt;p&gt;I have an open support issue with Western Digital to see if they have a fix for this issue. If there were some way that we could change the way the disk behaves under OSX, we could forego the additional software, which would be great. I've also heard of a utility called wdidle, which allegedly lets you write new idle settings to the hard drive. However, I was unable to find any official site for this software, so I'm not using it.&lt;/p&gt;&lt;p&gt;Finally, I would like to thank two people. First, &lt;a href="http://dougitdesign.com/blogs/blog_12_08_mac-clicking-hdd-hard-drive-noise-hdapm.html" title="Does your Apple notebook hard drive (HDD) ever sound like little mice are playing table tennis inside of it? Or, why your HDD might be pre-programmed for quick failure."&gt;Doug Aghassi's post&lt;/a&gt; really explained the symptoms that he was experiencing and put me on the right track for solving the problem. Thanks, Doug. Also, Bryce McKinlay was kind enough not only to write the hdapm utility, but also to answer the questions that I emailed to him. Thanks, Bryce.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-1992130746802689436?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1992130746802689436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1992130746802689436' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1992130746802689436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1992130746802689436'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/11/fixing-hard-disk-clicking-aggressive.html' title='Fixing hard disk clicking / aggressive head parking on Mac OS X'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-9141621333125109195</id><published>2009-09-20T11:57:00.000-04:00</published><updated>2009-09-20T11:57:20.545-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miniatures'/><category scheme='http://www.blogger.com/atom/ns#' term='Warhammer 40k'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><title type='text'>More Painting Space Marines</title><content type='html'>&lt;p&gt;I have an update to my Space Marine painting. I've continued to shade the marines. After the previous coat of 1:1 &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat160002&amp;amp;prodId=prod810878"&gt;Regal Blue&lt;/a&gt; to &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat160002&amp;amp;prodId=prod810879"&gt;Ultramarine Blue&lt;/a&gt;, I added the following:&lt;/p&gt;&lt;p&gt;&lt;ul&gt;&lt;li&gt;1:2 Regal Blue to Ultramarine Blue&lt;/li&gt;&lt;li&gt;Ultramarine Blue&lt;/li&gt;&lt;li&gt;2:1 Ultramarine Blue to &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat160002&amp;amp;prodId=prod810893"&gt;Codex Grey&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Each coat is painted in a slightly smaller area. The goal is to shade the model to match an imaginary light source. In my case, my light source is directly above the model. Here are the results:&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_yWS30lH8nfY/SrZNRvu_ZNI/AAAAAAAAALc/IG6ve5HAuiM/Shaded1.png" /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_yWS30lH8nfY/SrZNVOq65AI/AAAAAAAAALk/svx30EMPksk/Shaded2.png" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I'm now only painting 2 marines. I'm going to finish them before starting others, so that I can improve on my technique for those later models.&lt;/p&gt;&lt;p&gt;I also started painting my Tyranid spores. This has been basecoated with Chaos Black spray, then painted with &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat160002&amp;amp;prodId=prod810857"&gt;Blood Red&lt;/a&gt;, then washed with Chestnut Ink. I had tried Red Ink, but unfortunately, that color is too close to Blood Red.&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_yWS30lH8nfY/SrZNYsyIyHI/AAAAAAAAALs/ZziBtu2vjr0/Inked_Spore.png" /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-9141621333125109195?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/9141621333125109195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=9141621333125109195' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/9141621333125109195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/9141621333125109195'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/09/more-painting-space-marines.html' title='More Painting Space Marines'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_yWS30lH8nfY/SrZNRvu_ZNI/AAAAAAAAALc/IG6ve5HAuiM/s72-c/Shaded1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8024780350600957176</id><published>2009-09-16T22:00:00.000-04:00</published><updated>2009-09-16T22:00:31.470-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='miniatures'/><category scheme='http://www.blogger.com/atom/ns#' term='Warhammer 40k'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><title type='text'>Painting Space Marines</title><content type='html'>&lt;p&gt;For some reason, I got an itch to paint some &lt;a href="http://en.wikipedia.org/wiki/Warhammer_40,000"&gt;Warhammer 40k&lt;/a&gt; figures. I've been slowly assembling them over... I don't know, maybe 2 years. Hey, I have a lot of things that I do in my free time. Anyway, I finally got around to priming them the other day, and I've been painting like a crazy person since then.&lt;/p&gt;&lt;p&gt;Now, I should mention that these aren't the first figures I've painted. I had painted a squad of 5 marines that came in a box along with 6 paints and a brush. Here's one of them.&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_yWS30lH8nfY/SrGQ0mSJh1I/AAAAAAAAAKU/B08358ub1sE/Old.png" /&gt;&lt;/p&gt;&lt;p&gt;That was a good learning experience. This time, I have quality glue, basing material, spray primer, a variety of brushes, and lots of paints and inks. I'm posting some photos of various steps of the process. Hopefully, by putting them here, I will actually finish painting them.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;This space marine has been assembled, based, and primed. The basing material is sand and rock, glued into place with regular white glue. The whole model is sprayed with a flat black primer, to help the other paint stick better.&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_yWS30lH8nfY/SrGTp23CQhI/AAAAAAAAALU/jWVMvua-T3o/Primed.png" /&gt;&lt;/p&gt;&lt;p&gt;This marine has had his armor painted with &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat160002&amp;prodId=prod810879"&gt;Ultramarine Blue&lt;/a&gt;. Actually, his feet are missing some paint, but I'll get around to that. Additionally, the black ground cover has been drybrushed to look like sand and rocks. I started with rocky sand, painted it black, then painted it to look like rocky sand again. Crazy? Probably.&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_yWS30lH8nfY/SrGQ3yMTURI/AAAAAAAAAKc/ZnMEQGVgbik/Basecoated.png" /&gt;&lt;/p&gt;&lt;p&gt;This marine has been washed with a blue ink (which I think has been replaced with &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat1210000&amp;prodId=prod1340014"&gt;Asurmen Blue Wash&lt;/a&gt;). Ink is used because it settles in the crevasses and provides great depth.&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_yWS30lH8nfY/SrGQ6YAd0VI/AAAAAAAAAKk/JtZU3vX8cdM/Inked.png" /&gt;&lt;/p&gt;&lt;p&gt;This marine has had his armor panels painted with a 50/50 mix of &lt;a href="http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat160002&amp;prodId=prod810878"&gt;Regal Blue&lt;/a&gt; and Ultramarine Blue. By leaving a slight gap around the edges, the blue wash peeks through the armor panels, and this looks great.&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_yWS30lH8nfY/SrGQ8btoWgI/AAAAAAAAAKs/GCe-VLa17Ww/Layered.png" /&gt;&lt;/p&gt;&lt;p&gt;For those who don't know much about 40k, these figures are pretty small. Here's a comparison shot.&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_yWS30lH8nfY/SrGQ-3KTnfI/AAAAAAAAAK0/BNMbzqw79oc/Comparison.png" /&gt;&lt;br /&gt;Now imagine trying to paint those eye lenses. Yeah, I'm not looking forward to it, either. Besides the eyes, I still have a lot to do. I plan to put another few layers of blue on the armor, paint the shoulder pad edges, drybrush the metal pieces, and so on. If anybody reads this and has feedback or suggestions, I'd love to hear from you!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8024780350600957176?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8024780350600957176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8024780350600957176' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8024780350600957176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8024780350600957176'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/09/painting-space-marines.html' title='Painting Space Marines'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_yWS30lH8nfY/SrGQ0mSJh1I/AAAAAAAAAKU/B08358ub1sE/s72-c/Old.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1195507553982175756</id><published>2009-08-07T13:36:00.003-04:00</published><updated>2009-08-07T13:40:32.955-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='suggestions'/><category scheme='http://www.blogger.com/atom/ns#' term='Blogger'/><category scheme='http://www.blogger.com/atom/ns#' term='criticism'/><title type='text'>I wish Blogger would let me rename tags</title><content type='html'>Occasionally, if you read this via RSS, you will notice that I repost old articles. This isn't intentional - any time I edit or retag a post in Blogger, it puts it back on the feed. I don't see any way to say "quietly republish this post". The same problem occurs if I want to rename a tag - I have to remove the tag from all posts, and then re-add it to all posts. Please, Google, add features to Blogger to make this less painful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-1195507553982175756?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1195507553982175756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1195507553982175756' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1195507553982175756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1195507553982175756'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/08/i-wish-blogger-would-let-me-rename.html' title='I wish Blogger would let me rename tags'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1300947397824288764</id><published>2009-08-07T12:49:00.005-04:00</published><updated>2009-08-07T13:36:43.549-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='T-Mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='suggestions'/><category scheme='http://www.blogger.com/atom/ns#' term='criticism'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>T-Mobile Visual Voicemail Problems</title><content type='html'>&lt;p&gt;T-Mobile recently released their &lt;a href="http://www.cyrket.com/package/com.oz.mobile.android.voicemail.application" title="Cyrket - T-Mobile Visual Voicemail"&gt;Visual Voicemail&lt;/a&gt; application in the &lt;a href="market://search?q=pname:com.oz.mobile.android.voicemail.application"&gt;Android market&lt;/a&gt;. It had some launch problems, but those are mostly smoothed out at this point. The app works pretty well, and I'm glad that they have finally implemented it. However, the app does have its share of first-release problems. They are listed here, in the order that I hope T-Mobile addresses them.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;VVM doesn't work with Wifi.&lt;/strong&gt; Most people probably have Wifi enabled on their phones. After all, it's the most efficient (both bytes / time and power / byte) way to tranfer data. However, VVM doesn't work with Wifi. It will neither notify you of new voicemails, nor will it download new messages. In order to make it work, you need to &lt;ol&gt;&lt;li&gt;Turn off Wifi&lt;/li&gt;&lt;li&gt;Wait or click "Synchronize Voicemails"&lt;/li&gt;&lt;li&gt;Turn Wifi back on&lt;/li&gt;&lt;/ol&gt;Call me crazy, but that's just stupid. At the very least, the "Synchronize Voicemails" button should do those steps for you, similar to the &lt;a href="http://apps.t-mobile.com/"&gt;My Account&lt;/a&gt; app. There has been some FUD about the reason for this omission. Some people claim that it's for "security". I'm going to make this perfectly clear: there is no security-related reason to prevent people from getting their voicemails over Wifi. It's easy to encrypt data that is transmitted over the internet. There are a number of possible reasons they don't support Wifi. It might just be too much work. Maybe they haven't had time yet. It might be cost prohibitive. Perhaps there is a technical restriction - they would need to read the SIM's IMSI into the app, which Android might not allow. Whatever the case, it's not a security issue.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;VVM has a separate notification icon.&lt;/strong&gt; Every time you get a new voicemail, you get the standard voicemail icon. In addition, you get a new VVM icon. For now, this is fine. If I have Wifi enabled, I still get notified of a new voicemail (via the standard voicemail icon). When the Wifi issue is fixed, however, I would like to see the new icon go away. The notification bar is crowded enough.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;VMM uses the wrong audio stream.&lt;/strong&gt; VVM uses the "media" audio stream. Many people complain that prevents you from using a bluetooth headset to listen to your VM. I don't have a bluetooth headset, so I can't confirm this. It should use the "phone" audio stream.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The UI needs polish.&lt;/strong&gt; There are some small look-and-feel issues:&lt;ol&gt;&lt;li&gt;The VVM status bar icon doesn't match the &lt;a href="http://developer.android.com/guide/practices/ui_guidelines/icon_design.html" title="Icon Design Guidelines | Android Developers"&gt;Android UI Guidelines&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;After pressing the "Synchronize Voicemails" button, there is no feedback. No spinner, no progress bar, nothing.&lt;/li&gt;&lt;li&gt;The long-press context menu on a VM does not include a "delete" option (only Open, Reply As, and Copy To)&lt;/li&gt;&lt;li&gt;The buttons that appear when you press the "Menu" button have no icons.&lt;/li&gt;&lt;li&gt;The "Copy to" screen is a little too technical. The file name defaults to vm&lt;i&gt;n&lt;/i&gt; (i.e. vm0, vm1, vm2). It should instead default to something like "Voicemail from John Smith on 22 Jul, 2009". In addition, the save directory defaults to "/sdcard". Should users really be exposed to UNIX pathnames? Clicking the Save in Directory dropdown presents me with a file browser for my SD card. For me, this lists locations like ".Trashes" (I use a Mac), espeak-data (the data files for the &lt;a href="http://eyes-free.googlecode.com/" title="eyes-free - Project Hosting on Google Code"&gt;Text-To-Speech engine&lt;/a&gt;), "where" (the data for &lt;a href="http://where.com/" title="WHERE GPS Mobile Application, iPhone App &amp;amp; Location Based Services  Development Platform"&gt;Where&lt;/a&gt;), and other places that I probably shouldn't be saving random files. Do I really need to be able to specify the location to save the voicemail? Why not just save everything to /sdcard/voicemails? Or at least, why not assume that all voicemails get saved to /sdcard/voicemails or a subdirectory (i.e. you can't save a voicemail outside this directory, only inside)?&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Initial, first-run experience is lousy.&lt;/strong&gt; When I first installed and ran the app, it wasn't able to connect to the server. After disabling Wifi, it worked. I was taken to a set-up screen, but then got distracted by something and hit the back button. When I relaunched the application, the set-up screen wasn't presented. This worried me (is there some setup that needed to occur), so I uninstalled the app and re-installed it. I don't think I did any harm, but the app didn't behave as I expected, so I didn't know what to think.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Deleting doesn't always work.&lt;/strong&gt; I'm going to chalk this up to glitch behavior. The first time I used the app, I went through and deleted some old messages. Then I went into the analog voicemail system, and they were back! I deleted them a second time, and now they're really gone. &lt;em&gt;shrug&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Now, I don't mind all of those problems. I'm glad that T-Mobile finally released a VVM app. I'm glad that they released it early, warts and all. I hope that they are not done working on it. For me, the Wifi issue is huge. I'm connected to Wifi 90% of the time, and that means that the VVM app doesn't function as a voicemail app 90% of the time. I suspect many other people are in the same boat as me. Furthermore, Google Voice is coming. If the Wifi issue isn't fixed by the time GV is generally available, I might just jump ship, and T-Mobile doesn't want me to do that. I understand if T-Mobile can't fix this on their own - they might need support from Google. Still, every carrier is going to want to provide VVM, and it would behoove Google to provide whatever support necessary.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-1300947397824288764?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1300947397824288764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1300947397824288764' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1300947397824288764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1300947397824288764'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/08/t-mobile-visual-voicemail-problems.html' title='T-Mobile Visual Voicemail Problems'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4003321474349896931</id><published>2009-07-18T20:37:00.004-04:00</published><updated>2009-07-18T20:49:57.577-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Stack Overflow'/><title type='text'>Getting Started with Stack Overflow</title><content type='html'>&lt;p&gt;I joined &lt;a href="http://stackoverflow.com/" title="Stack Overflow"&gt;Stack Overflow&lt;/a&gt; shortly after it launched, but I didn't do anything with it. I found it in search results here and there, but I never asked any questions. I would have done more, but new users are pretty helpless. You can't vote up or down, you can't comment on answers, you can't post an answer with more than 1 link, etc. It's almost like you're not wanted. Compared to the relative freedom of Wikipedia, it was really demoralizing to me.&lt;/p&gt;&lt;p&gt;I decided tonight to actually try to get some reputation. Most of the interesting stuff happens around 50 reputation, so that's my goal. I answered 2 questions this evening. Suddenly, my rep is skyrocketing. I'm &lt;a href="http://stackoverflow.com/users/120278/daniel-yankowsky" title="User Daniel Yankowsky - Stack Overflow"&gt;at 31&lt;/a&gt; right now, and I bet that will continue to climb on its own. It seems that people are very willing to vote your answers up if they are relevant. As you can see, it shouldn't be hard to get to the point of actually being able to contribute.&lt;/p&gt;&lt;p&gt;So, if you want to get started with Stack Overflow, here are my suggestions.&lt;ol&gt;&lt;li&gt;Go to &lt;a href="http://stackoverflow.com/questions?sort=newest" title="Hottest Questions - Stack Overflow"&gt;the newest questions page&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Find something that you know something about. Don't troll, and don't post to random topics about which you know nothing.&lt;/li&gt;&lt;li&gt;Write an answer.&lt;/li&gt;&lt;/ol&gt;That should just about do it. Don't despair, it's easier than it initially seems.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4003321474349896931?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4003321474349896931/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4003321474349896931' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4003321474349896931'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4003321474349896931'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/07/getting-started-with-stack-overflow.html' title='Getting Started with Stack Overflow'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6903109504117028080</id><published>2009-07-16T02:06:00.003-04:00</published><updated>2009-07-16T02:34:51.020-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='repair'/><category scheme='http://www.blogger.com/atom/ns#' term='criticism'/><category scheme='http://www.blogger.com/atom/ns#' term='Xbox 360'/><title type='text'>Fixing the Xbox 360 's Grinding Noise / Tray Ejection Problem</title><content type='html'>&lt;p&gt;I spent an evening performing unexpected surgery on my Xbox 360. When I put a game in, the drive made the most horrible grinding noise. On top of that, the drive would not stay closed. The tray would almost always eject seconds after being closed. Research led me to conclude that the rare earth magnet that is part of the disc clamp had probably become unglued. Since my initial warranty has long since expired and the red ring of death warranty only has another year, I decided to crack the case myself.&lt;/p&gt;&lt;p&gt;It's not worth going through the details, but I did find two useful videos. The first is &lt;a href="http://www.youtube.com/watch?v=9WQP66Xvvko"&gt;an overview of the problem&lt;/a&gt;. The second is &lt;a href="http://www.youtube.com/watch?v=Uj2A7mGGjuY"&gt;a good tutorial on opening the 360's case&lt;/a&gt;. I used some Zap-A-Gap brand contact adhesive that I had laying around to actually reattach the magnet.&lt;/p&gt;&lt;p&gt;I felt quite proud to have diagnosed, researched, and fixed the problem on my own (without sending my console to Microsoft for repairs). $100 plus shipping just to have some intern apply some glue is a little extreme. So many people have had this problem that YouTube videos just refer to it as "the grinding noise problem." Either Hitachi (the drive manufacturer) just made a lousy drive, or Microsoft didn't correctly anticipate the effect that their game furnace would have on the glue that Hitachi used. I don't know who is to blame, but Microsoft should extend their warranty on the 360 to 3 years for all defects, not just those that cause the &lt;a href="http://en.wikipedia.org/wiki/Red_ring_of_death"&gt;red LEDs to light up in a circular fashion&lt;/a&gt;. I don't expect my car to wear out in 2 years, and I use it every day. My game console shouldn't wear out, either.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6903109504117028080?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6903109504117028080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6903109504117028080' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6903109504117028080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6903109504117028080'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/07/fixing-xbox-360-s-grinding-noise-tray.html' title='Fixing the Xbox 360 &apos;s Grinding Noise / Tray Ejection Problem'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-9160851242552947894</id><published>2009-07-15T00:02:00.003-04:00</published><updated>2009-07-15T00:15:56.443-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='praise'/><category scheme='http://www.blogger.com/atom/ns#' term='eBay'/><title type='text'>Feedback Works!</title><content type='html'>&lt;p&gt;It's hard to say if this is coincidence, but it's worth mentioning. A few weeks ago, I sent eBay feedback on their new site design. My gripe was that they were nesting scrollable areas within scrollable areas. As I was using the site today, it dawned upon me that the bad behavior was gone. Somebody actually fixed it. Perhaps it was my email; perhaps it was the combined voice of thousands of users; perhaps some developer just realized that there was a better way. Whatever the case, I'm glad that eBay made the change, and I like to think that I helped in some way. Thanks, eBay!&lt;/p&gt;&lt;p&gt;Before, things just felt wrong. I would be scrolling the page, and it would mysteriously stop. This happened on every single page, it seemed. When they switched to good design, I didn't even notice at first. It just felt natural. This brings to light a sort of design axiom: design successes are invisible, design flaws are glaring.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-9160851242552947894?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/9160851242552947894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=9160851242552947894' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/9160851242552947894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/9160851242552947894'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/07/feedback-works.html' title='Feedback Works!'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-7347370647075452690</id><published>2009-06-28T19:40:00.001-04:00</published><updated>2009-06-28T19:50:36.118-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='electronics'/><category scheme='http://www.blogger.com/atom/ns#' term='praise'/><category scheme='http://www.blogger.com/atom/ns#' term='positive'/><title type='text'>Excellent Customer Service at Adafruit</title><content type='html'>&lt;p&gt;I've been meaning to write this post for a while, but I just haven't gotten around to it. Well, that ends now! The time has come!&lt;/p&gt;&lt;p&gt;A little over a month ago, I ordered a couple of kits from &lt;a href="http://www.adafruit.com/" title="Adafruit Industries, Unique &amp;amp; fun DIY electronics and kits"&gt;Adafruit Industries&lt;/a&gt;. When the box arrived at my doorstep, it seemed a little too small. Once I opened the package, my fears were confirmed: they had only included one of the two kits.&lt;/p&gt;&lt;p&gt;I typed up a quick email to customer support that night, and figured I'd hear back in a few days time. After all, they are a small company (I think they are small. I don't really know.). The next morning, I was surprised to find this email in my mailbox:&lt;/p&gt;&lt;blockquote&gt;hi daniel -&lt;br /&gt;&lt;br /&gt;that's odd - we're going to send out a starter pack immediately -  and&lt;br /&gt;we will check the order to see what happened.&lt;br /&gt;&lt;br /&gt;cheers,&lt;br /&gt;adafruit&lt;/blockquote&gt;&lt;p&gt;That email was sent at 3:03AM. They immediately sent out the second kit. It arrived a couple of days later.&lt;/p&gt;&lt;p&gt;I was surprised and pleased with their customer service. They didn't question my claim, didn't beat around the bush, and didn't make me wait. They corrected their mistake as quickly as they could. Adafruit Industries is a top-class outfit. I will be ordering (many things) from them in the future.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-7347370647075452690?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/7347370647075452690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=7347370647075452690' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7347370647075452690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7347370647075452690'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/06/excellent-customer-service-at-adafruit.html' title='Excellent Customer Service at Adafruit'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1552375585449407726</id><published>2009-06-27T12:18:00.006-04:00</published><updated>2009-06-27T12:48:10.821-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Arduino'/><category scheme='http://www.blogger.com/atom/ns#' term='MacOS'/><title type='text'>Arduino librxtxserial.jnilib on MacOS</title><content type='html'>&lt;p&gt;After re-installing MacOS from scratch, I found that I was having problems launching Arduino. I got the following error:&lt;/p&gt;&lt;img src="http://lh4.ggpht.com/_yWS30lH8nfY/SkZGzLiZDTI/AAAAAAAAAIQ/Vh1cn5WvvR0/s800/Picture%201.png" /&gt;&lt;p&gt;In order to solve this, I followed &lt;a href="http://code.rancidbacon.com/Arduino"&gt; the instructions at rancidbacon.com&lt;/a&gt;. The solution was to use Finder to configure the application to launch in 32-bit mode. I quickly looked to see if I could modify the Info.plist file to enforce this behavior, but I don't see any way to do that.&lt;/p&gt;&lt;p&gt;Another &lt;a href="http://tech.element77.com/2009/06/arduino-problems-with.html"&gt;solution from technobabble&lt;/a&gt; is to replace the library inside the Arduino app bundle with a 1.6 compatible version.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-1552375585449407726?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1552375585449407726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1552375585449407726' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1552375585449407726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1552375585449407726'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/06/arduino-librxtxserialjnilib-on-macos.html' title='Arduino librxtxserial.jnilib on MacOS'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_yWS30lH8nfY/SkZGzLiZDTI/AAAAAAAAAIQ/Vh1cn5WvvR0/s72-c/Picture%201.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3706162487954424539</id><published>2009-06-18T00:09:00.003-04:00</published><updated>2009-06-18T00:13:47.627-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Gents with Beards'/><title type='text'>Quick, Over There!</title><content type='html'>&lt;p&gt;I'm going to try to continue posting stuff here, but it's worth mentioning that I will also be posting over at the &lt;a href="http://gentswithbeards.blogspot.com/"&gt;Gents with Beards&lt;/a&gt; blog. Anything related to the iPhone will go there (though I will make brief mention of it here). Anything else might go there, might come here, or might get cross-posted.&lt;/p&gt;&lt;/p&gt;I made a couple of posts so far, related to my experiences with &lt;a href="http://gentswithbeards.blogspot.com/2009/06/openal-in-iphone-simulator.html"&gt;OpenAL in the iPhone Simulator&lt;/a&gt; and differences in the &lt;a href="http://gentswithbeards.blogspot.com/2009/06/iphone-vs-android-initial-developer.html"&gt;Initial Developer Experience between iPhone and Android&lt;/a&gt;. I hope to write one or two about Git, and who knows what after that.&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3706162487954424539?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3706162487954424539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3706162487954424539' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3706162487954424539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3706162487954424539'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/06/quick-over-there.html' title='Quick, Over There!'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4927452117801325922</id><published>2009-05-15T00:13:00.003-04:00</published><updated>2009-06-27T12:48:10.821-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>JTextArea and javax.swing.text.Document</title><content type='html'>&lt;p&gt;I was playing with a JTextArea in my Swing project today. My underlying data is actually quite simple (a list of names and quotes), and my JTextArea is read-only. Therefore, I decided to try to make my own class that implements javax.swing.text.Document, but whose internal structure more closely models my real... model.&lt;/p&gt;&lt;p&gt;I had a hard time getting started. From poking around in Document's Javadocs and in related Java source code, it appears that Document is a very general concept. It seems to have originally been intended to model an SGML document's structure - or something like that. The Document interface lets you navigate a tree structure that represents the document. However, when it comes to modeling data for a JTextArea, things are much simpler. Instead, it appears that JTextComponent (the base class of JTextArea) assumes that the Document models a list of lines of text. That is to say, it seems to expect that the Document has one root element which contains a separate element for each line. This is most obvious from JTextArea's getLineCount() method:&lt;/p&gt;&lt;pre class="code"&gt;public int getLineCount() {&lt;br /&gt;    Element map = getDocument().getDefaultRootElement();&lt;br /&gt;    return map.getElementCount();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Now, the problem with all of this is that Document is a pretty complicated interface. On top of that, since SGML is a tree-oriented structure, it is not quite intuitive to map the concepts of "stream of text" to "tree-shaped structure". More problematically, I didn't see anything in the documentation to even begin to shed some light on this unspoken relationship. I stumbled upon it while examining stack traces from my first cut. I vaguely recall this being much easier in the little bit of Cocoa that I dabbled in, but I can't be sure. Maybe I'm just crazy - there are certainly a number of other concrete, Document-derived classes out there.&lt;/p&gt;&lt;p&gt;So remember, if you want to implement Document, make sure to model your document as a list of lines of text.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4927452117801325922?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4927452117801325922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4927452117801325922' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4927452117801325922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4927452117801325922'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/05/jtextarea-and-javaxswingtextdocument.html' title='JTextArea and javax.swing.text.Document'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3330142223456963948</id><published>2009-04-07T20:56:00.003-04:00</published><updated>2009-04-07T22:24:16.147-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='praise'/><category scheme='http://www.blogger.com/atom/ns#' term='macheist'/><category scheme='http://www.blogger.com/atom/ns#' term='positive'/><title type='text'>Something Nice</title><content type='html'>&lt;p&gt;I don't like that most of my blog posts have a negative tone. I'd like to balance that by talking a bit about something that was really cool. Let me wax lyrically about &lt;a href="http://www.macheist.com/" title="MacHeist &amp;raquo; Bundle"&gt;MacHeist&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;After buying the last MacHeist bundle a year ago, I got put on the MacHeist mailing list. As a result, I was able to participate in the missions that they've been putting out for the past few months. Between the free apps I got from completing the missions, the apps in the actual heist, referring 2 friends, and tweeting once, I managed to score 40 apps worth a theoretical value of almost $1700. It cost me $33 (I got a $6 discount by completing in the missions), and a few hours of my time. Let me go into a little more detail about the whole thing.&lt;/p&gt;&lt;p&gt;In order to drum up support, the MacHeist team starts by putting out challenges for several months before the actual sale. In order to solve these riddles, you need to do a little internet sleuthing. This year, they got the cooperation of &lt;a href="http://www.veronicabelmont.com/" title="Veronica Belmont"&gt;Veronica Belmont&lt;/a&gt;, &lt;a href="http://mostlylisa.com/" title="MostlyLisa | Photography, Videos &amp;amp; Geeky Stuff."&gt;Lisa Bettany&lt;/a&gt;, and &lt;a href="http://chris.pirillo.com/" title="Chris Pirillo"&gt;Chris Pirillo&lt;/a&gt;. People work hard to figure them out but, if you're lazy, you can just follow the walkthroughs that people post. I usually do about 50/50 - working on the challenge until I'm out of ideas, then go read what smarter people than me wrote. By doing this, I got $541 of software (and a $6 off rebate on the actual bundle) for just a few hours of time. Lisa Bettany also starred in a handful of mission briefings as Sophia, the Eastern European secret agent trying to save the world from time glitches. A little campy, but entertaining nonetheless.&lt;/p&gt;&lt;p&gt;Of course, there's also the heist itself. The whole bundle was introduced this year in a 90ish minute live streaming show with an overview of every app. They started this one by selling 8 apps for $39. From each sale, 25% of all sales go to charity. When they reach certain charity milestones, they add more apps. This year, there were 3 milestones. The last was $500,000 for charity, and that unlocked the final 2 apps. It was a little scary this year, because sales stagnated after a couple of days. The last two apps, one of which was Espresso, were still locked. Fortunately, there was a last-minute sales frenzy, and now they're waaay over their final unlock milestone. To try to incentivize people who were sitting on the fence, they added two more apps during the sale, for a total of 14.&lt;/p&gt;&lt;p&gt;Since Twitter is popular with the kids these days, MacHeist took the opportunity to reach tons of people by hyjacking the social experience. If you were willing to tweet a short message to get the word out about MacHeist, they gave you two more applications. Was I just helping The Man by giving free advertising? No! I wanted other people to have the chance to get these apps. This is the tweet they asked me to post:&lt;blockquote&gt;I bought the @MacHeist 3 Bundle. 12 Top Mac apps worth $900+ for just $39 AND I just got Delicious Library 2 FREE!&lt;/blockquote&gt; That's not too bad!&lt;/p&gt;&lt;p&gt;Finally, I managed to refer two of my friends to the sale, and I got an additional 2 apps from that.&lt;/p&gt;&lt;p&gt;Now, I realize that I didn't actually get $1700 of worth from this bundle. I will probably never even start some of these apps. However, if you can find even 2 apps that you want, this deal makes sense. In my case, I had my eye on several of these apps for a while, and just never got around to buying them. Also, keep in mind that a big pile of them were totally free. I could have walked away with a bunch of software for just a few hours of puzzle solving (or walkthrough-reading). This sale always amazes me. Past sales have included Delicious Library 1, &lt;a href="http://macromates.com/" title="TextMate — The Missing Editor for Mac OS X"&gt;TextMate&lt;/a&gt;, &lt;a href="http://macrabbit.com/cssedit/" title="MacRabbit - CSSEdit - Web 2.0 in Style"&gt;CSSEdit&lt;/a&gt;, &lt;a href="http://www.pixelmator.com/" title="Pixelmator"&gt;PixelMator&lt;/a&gt;, and other great apps. Good job, MacHeist.&lt;/p&gt;&lt;p&gt;Bundle apps&lt;ul&gt;&lt;li&gt;&lt;a href="http://flyingmeat.com/acorn/" title="Acorn, the image editor for humans."&gt;Acorn&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.pangeasoft.net/cromag/index.html" title="Pangea Software: Cro-Mag Rally for Mac OS X"&gt;Cro-Mag Rally&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.boinx.com/boinxtv/overview/" title="Boinx Software - BoinxTV - Overview"&gt;BoinxTV&lt;/a&gt; Sponsored Edition (which means that you have to indicate that you used Boinx to record your video, which kinda sucks)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.macrabbit.com/espresso/" title="MacRabbit - Espresso"&gt;Espresso&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.equinux.com/us/products/isale/index.html" title="equinux - Website"&gt;iSale&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.kinemac.com/products/kinemac/" title="Kinemac - Kinemac Product Page"&gt;Kinemac&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.realmacsoftware.com/littlesnapper/" title="LittleSnapper - Screen and Web Snapping for Mac OS X Leopard"&gt;LittleSnapper&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://ecamm.com/mac/phoneview/" title="Ecamm Network: PhoneView - Add Notes to iPhone and iPod Touch - Backup to iPhone - Download Notes, Access SMS, Contacts, Call History"&gt;PhoneView&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.acqualia.com/picturesque/" title="Picturesque - Make Images Gorgeous"&gt;Picturesque&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.acaciatreesoftware.com/" title="Acacia Tree Software - SousChef: Your Digital Cooking Assistant"&gt;SousChef&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.potionfactory.com/thehitlist/" title="The Hit List | Potion Factory"&gt;The Hit List&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.acrylicapps.com/times/" title="Acrylic - Times"&gt;Times&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.ambrosiasw.com/utilities/wiretap/" title="WireTap Studio | Ambrosia Software, Inc."&gt;WireTap Studio&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://2dboy.com/games.php" title="2D Boy: Games"&gt;World of Goo&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Apps for completing missions (these only took a few hours to get, but were only available for a limited time)&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.freeverse.com/games/game/?id=27" title="Freeverse: Games: Hearts Deluxe"&gt;3D Hearts&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://blackpixelluminance.com/agentcraig.html" title="Agent Craig"&gt;Agent Craig&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.edgerift.com/products/allsecure/" title="EdgeRift - AllSecure (Mac Password &amp;amp; Information Manager)"&gt;AllSecure&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://illuminex.com/software/games/babelblox.html" title="illumineX :: software and games for Mac, iPhone and iPod Touch"&gt;BabelBloX&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://blitzapp.com/" title="Blitz - Your Life on the Mac, Now Faster."&gt;Blitz&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.ironicsoftware.com/fresh/index.html" title="Fresh"&gt;Fresh&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://hyperspacesapp.com/" title="Hyperspaces - The Ultimate Way to Customise Your Spaces!"&gt;Hyperspaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.danholt4mac.eu/english/38/34/34/181001/shopartikel.html" title="iStock | danholt consulting services"&gt;iStock&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.kavasoft.com/KavaTunes/" title="KavaTunes"&gt;KavaTunes&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.macdust.com/" title="MacDust 3"&gt;MacDust&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.frogames.com/minione_racing/" title="MiniOne Racing - Racing game for Mac &amp;amp; PC"&gt;MiniOne Racing&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://stuntsoftware.com/Overflow/" title="Stunt Software - Overflow"&gt;Overflow&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.koingosw.com/products/pandoraspests.php" title="Pandoras's Pests &amp;mdash; Save yourself and the earth from certain peril!"&gt;Pandora's Pests&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.jumsoft.com/process/" title="Jumsoft |   Process"&gt;Process&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.atebits.com/scribbles/" title="atebits - Scribbles"&gt;Scribbles&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.wonderwarp.com/shovebox/" title="ShoveBox | Wonder Warp Software"&gt;ShoveBox&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.randomsequence.com/software/sticky-notes/" title="Sticky Notes"&gt;Sticky Notes&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.timepost2.com/" title="Timepost   &amp;raquo; Home"&gt;Timepost&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.ergonis.com/products/typinator/" title="Automatic text software - faster typing and fewer errors with Typinator"&gt;Typinator&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.celmaro.com/webbla.html" title="Celmaro | Webbla"&gt;Webbla&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Apps for tweeting about the bundle (only available after buying the bundle)&lt;ul&gt;&lt;li&gt;&lt;a href="http://delicious-monster.com/" title="Delicious Library 2"&gt;Delicious Library 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.introversion.co.uk/multiwinia/" title="Multiwinia - Survival of the Flattest"&gt;Multiwinia&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Apps for referring others (only available after buying the bundle)&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.koingosw.com/products/bundle_utilitypackage.php" title="Utility Package - Licenses for all Koingo Software Titles!"&gt;Koingo Utility Package&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.ambrosiasw.com/games/pop-pop/" title="pop-pop | Ambrosia Software, Inc."&gt;Pop-Pop&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3330142223456963948?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3330142223456963948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3330142223456963948' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3330142223456963948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3330142223456963948'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/04/something-nice.html' title='Something Nice'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-933460826935857989</id><published>2009-02-25T22:44:00.004-05:00</published><updated>2009-08-07T13:36:32.899-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>Android SDK + eBay SDK</title><content type='html'>&lt;p&gt;I haven't done anything with Android in a while, so I thought I would see whether I could get my phone talking to eBay. I grabbed the eBay SDK for Java, stuck it in my project, wrote a small demo app, tried it out, and BOOM!&lt;/p&gt;&lt;pre class="code"&gt;VFY: unable to resolve new-instance 1516 (Ljavax/swing/event/EventListenerList;) in Lcom/ebay/sdk/ApiCredential;&lt;br /&gt;VFY:  rejecting opcode 0x22 at 0x0015&lt;br /&gt;VFY:  rejected Lcom/ebay/sdk/ApiCredential;.&amp;lt;init&amp;gt; ()V&lt;br /&gt;Verifier rejected class Lcom/ebay/sdk/ApiCredential;&lt;br /&gt;Shutting down VM&lt;br /&gt;threadid=3: thread exiting with uncaught exception (group=0x4000fe68)&lt;br /&gt;Uncaught handler: thread main exiting due to uncaught exception&lt;br /&gt;java.lang.VerifyError: com.ebay.sdk.ApiCredential&lt;br /&gt;    at com.ebay.sdk.ApiContext.&amp;lt;init&amp;gt;(ApiContext.java:41)&lt;br /&gt;    at org.balefrost.bodacious.Bodacious.onCreate(Bodacious.java:75)&lt;br /&gt;    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1122)&lt;br /&gt;    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2104)&lt;br /&gt;    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2157)&lt;br /&gt;    at android.app.ActivityThread.access$1800(ActivityThread.java:112)&lt;br /&gt;    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1581)&lt;br /&gt;    at android.os.Handler.dispatchMessage(Handler.java:88)&lt;br /&gt;    at android.os.Looper.loop(Looper.java:123)&lt;br /&gt;    at android.app.ActivityThread.main(ActivityThread.java:3739)&lt;br /&gt;    at java.lang.reflect.Method.invokeNative(Native Method)&lt;br /&gt;    at java.lang.reflect.Method.invoke(Method.java:515)&lt;br /&gt;    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)&lt;br /&gt;    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)&lt;br /&gt;    at dalvik.system.NativeStart.main(Native Method)&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;It looks like it depends on a Swing class (&lt;a href="http://java.sun.com/javase/6/docs/api/javax/swing/event/EventListenerList.html"&gt;EventListenerList&lt;/a&gt;), but Swing isn't available on Android. I guess I'm rolling it by hand then. It's actually unfortunate that EventListenerList is a part of Swing - it looks generic enough that maybe it should be promoted to the core library. For that matter, I'm a little surprised that this made it past the apk builder. Perhaps that is outside the scope of the apk builder.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-933460826935857989?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/933460826935857989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=933460826935857989' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/933460826935857989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/933460826935857989'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/02/i-havent-done-anything-with-android-in.html' title='Android SDK + eBay SDK'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8543373866335809799</id><published>2009-01-28T19:14:00.002-05:00</published><updated>2009-01-28T19:29:25.811-05:00</updated><title type='text'>Looking For a Laptop Table</title><content type='html'>&lt;p&gt;I'm looking to get a small side table so that I can use my laptop in my living room without it burning my lap. I'm considering a &lt;a href="http://www.lapdawg.com/"&gt;LapDawg&lt;/a&gt;, but the $130 price tag isn't too appealing. Whatever I end up with, it won't be a &lt;a href="http://www.youtube.com/watch?v=ly0-Vbqyby8"&gt;Freedom Furniture Laptop Table II Mobile&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Does anybody have any suggestions?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8543373866335809799?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8543373866335809799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8543373866335809799' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8543373866335809799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8543373866335809799'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/01/looking-for-laptop-table.html' title='Looking For a Laptop Table'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-2575509027781170786</id><published>2009-01-18T17:06:00.002-05:00</published><updated>2009-01-18T17:17:39.461-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='vista'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='ui'/><title type='text'>Vista Taskbar Transparency</title><content type='html'>&lt;p&gt;One quick Vista UI tidbit. Window decorations (titlebars and borders) in Vista are normally translucent. When you maximize a window, &lt;a href="http://blogs.msdn.com/oldnewthing/archive/2008/10/01/8969394.aspx"&gt;its titlebar become opaque&lt;/a&gt;. The Vista taskbar is also normally translucent and, like window titlebars, also turns opaque whenever any window is maximized.&lt;/p&gt;&lt;p&gt;Interestingly, this is done even if the window is maximized on another screen. I recently switched from a dark background picture to a nearly white picture, and it's getting frustrating to see my taskbar alternate between black and gray for what seems like no good reason.&lt;/p&gt;&lt;p&gt;Ultimately, this is a small gripe. It's doesn't really get in the way. It's just odd. It reflects a lack of focus on fit and finish at Microsoft. I hope Windows 7 ends up being better.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-2575509027781170786?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/2575509027781170786/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=2575509027781170786' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2575509027781170786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2575509027781170786'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2009/01/vista-taskbar-transparency.html' title='Vista Taskbar Transparency'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8180968040451047552</id><published>2008-12-02T23:05:00.006-05:00</published><updated>2008-12-02T23:25:08.526-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='htcg1'/><title type='text'>G1 Camera Tricks</title><content type='html'>&lt;p&gt;The G1's camera seems to be slow. Really slow. As in several-seconds slow. I always wondered why; then I started to look at my pictures from the Macy's Thanksgiving Day Parade.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_yWS30lH8nfY/STYGEAhHYkI/AAAAAAAAAGE/If1DsgmVDKg/s1600-h/1227796151134.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://4.bp.blogspot.com/_yWS30lH8nfY/STYGEAhHYkI/AAAAAAAAAGE/If1DsgmVDKg/s400/1227796151134.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5275410679439254082" /&gt;&lt;/a&gt;&lt;p&gt;Ah. I see. I don't think that's because of the curvature of the lens (it's not curved THAT much) I was apparently panning the camera when I took this picture (to keep up with the float, though I didn't correctly account for the very long shutter delay, and the float ended up behind the pole). You are seeing the effect of the CCD "scanning". This reminds me of &lt;a href="http://www.sentex.net/~mwandel/tech/scanner.html" title="Building scanning camera from a flatbed scanner"&gt;this project&lt;/a&gt;, in which a dude builds a digital camera from a flatbed scanner. &lt;img src="http://www.sentex.net/~mwandel/tech/wacky_garage.jpg" style="float:right; margin: 0 0 10px 10px;"/&gt; This is a picture of his garage door opening and closing while he takes a single exposure.&lt;/p&gt;&lt;p&gt;I hope this isn't an actual limitation of the G1's hardware. My cheap &lt;a href="http://europe.nokia.com/A4430608" title="Nokia Europe - Nokia 6267 - Technical specifications"&gt;Nokia 6267&lt;/a&gt;, a feature phone, had an excellent 2MP camera that took pictures nearly instantaneously. So far, the G1's camera reminds me of my short experience with Windows Mobile 5's camera app - slow and clunky.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8180968040451047552?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8180968040451047552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8180968040451047552' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8180968040451047552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8180968040451047552'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/12/g1-camera-tricks.html' title='G1 Camera Tricks'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_yWS30lH8nfY/STYGEAhHYkI/AAAAAAAAAGE/If1DsgmVDKg/s72-c/1227796151134.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-5000499182921486376</id><published>2008-12-02T22:35:00.006-05:00</published><updated>2010-03-03T19:21:00.219-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>Using a Horizontal Progress Bar in Android</title><content type='html'>&lt;p&gt;I was working on my Android downloader application yesterday, when I ran into a bit of a snag. I wanted to display a list of downloads (similar to the Firefox downloads window). Each item in the list should show the file name and a horizontal progress bar. I started to implement this, and several hours later stumbled upon the arcane solution. I wanted to share this, both to help anybody who may be trying to do something similar, and also to illustrate how the lack of good documentation is hurting aspiring Android developers.&lt;/p&gt;&lt;p&gt;I thought that I could put a &lt;a href="http://code.google.com/android/reference/android/widget/ProgressBar.html" title="android.widget.ProgressBar - Android"&gt;ProgressBar&lt;/a&gt; instance in my xml, set it to be indeterminate, give it a max and a value, and be done. However, it wasn't nearly that easy. After a few hours of hunting on Google and the Android Groups, I stumbled upon the solution in an &lt;a href="http://books.google.com/books?id=lrf4xxsgmd8C&amp;amp;pg=PA127&amp;amp;lpg=PA127&amp;amp;dq=android+horizontal+progress+bar&amp;amp;source=web&amp;amp;ots=yuCKvJxT29&amp;amp;sig=X1k2Y6AUBNKnpH2UtrP1xXOYEmI&amp;amp;hl=en&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;resnum=2&amp;amp;ct=result#PPA125" title="The Busy Coder's Guide to Android ... - Google Book Search"&gt;online preview&lt;/a&gt; of &lt;a href="http://commonsware.com/Android/" title="CommonsWare: The Busy Coder's Guide to Android Development"&gt;The Busy Coder's Guide to Android Development&lt;/a&gt;. In short, I had to set the following on my ProgressBar's XML:&lt;pre class="code"&gt;style="?android:attr/progressBarStyleHorizontal"&lt;/pre&gt;WTF?&lt;/p&gt;&lt;p&gt;Let's try to break this down. The ? on the front indicates that this is a reference to something in the current theme. What is a theme? Well, Android's UI infrastructure is similar to Swing or HTML in that it tries to accommodate components whose size is not known until runtime. Android's components are also themeable (similar to the Swing &lt;a href="http://www.ibm.com/developerworks/java/library/j-synth/" title="Advanced Synth"&gt;Synth LAF&lt;/a&gt;). A theme is a collection of related component styles. You can control the theme used by your application &lt;a href="http://code.google.com/android/reference/android/R.styleable.html#AndroidManifestApplication_theme" title="android.R.styleable - Android"&gt;in the AndroidManifest.xml file&lt;/a&gt;. So, I think we can safely conclude that attr/progressBarStyleHorizontal is something whose actual value is defined in a particular theme.&lt;/p&gt;&lt;p&gt;If you take a peek in the &lt;a href="http://source.android.com/" title="Welcome ‎(Android Open Source Project‎)"&gt;android source code&lt;/a&gt;, inside frameworks/base/core/res/res/values/attrs.xml, you will find the following definition:&lt;pre class="code"&gt;&amp;lt;resources&amp;gt;&lt;br /&gt;    &amp;lt;declare-styleable name="Theme"&amp;gt;&lt;br /&gt;        &amp;lt;!-- snip --&amp;gt;&lt;br /&gt;        &amp;lt;attr name="progressBarStyleHorizontal" format="reference" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;OK, not very useful. However, in frameworks/base/core/res/res/values/themes.xml, we find:&lt;pre class="code"&gt;&amp;lt;resources&amp;gt;&lt;br /&gt;    &amp;lt;style name="Theme"&amp;gt;&lt;br /&gt;        &amp;lt;item name="progressBarStyleHorizontal"&amp;gt;@android:style/Widget.ProgressBar.Horizontal&amp;lt;/item&amp;gt;&lt;br /&gt;&lt;/pre&gt;Nowe we're getting somewhere. Finally, inside frameworks/base/core/res/res/values/styles.xml, we see:&lt;pre class="code"&gt;&amp;lt;resources&amp;gt;&lt;br /&gt;    &amp;lt;style name="Widget.ProgressBar.Horizontal"&amp;gt;&lt;br /&gt;        &amp;lt;item name="android:indeterminateOnly"&amp;gt;false&amp;lt;/item&amp;gt;&lt;br /&gt;        &amp;lt;item name="android:progressDrawable"&amp;gt;@android:drawable/progress_horizontal&amp;lt;/item&amp;gt;&lt;br /&gt;        &amp;lt;item name="android:indeterminateDrawable"&amp;gt;@android:drawable/progress_indeterminate_horizontal&amp;lt;/item&amp;gt;&lt;br /&gt;        &amp;lt;item name="android:minHeight"&amp;gt;20dip&amp;lt;/item&amp;gt;&lt;br /&gt;        &amp;lt;item name="android:maxHeight"&amp;gt;20dip&amp;lt;/item&amp;gt;&lt;br /&gt;    &amp;lt;/style&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;What's going on here? Well, it looks like themes.xml defines a theme as a collection of styles. Along with that, styles.xml specifies a set of attributes that should be applied to a particular XML element. By saying &lt;pre class="code"&gt;&amp;lt;ProgressBar style="?android:attr/progressBarStyleHorizontal"&amp;gt;&lt;/pre&gt; we are really saying &lt;pre class="code"&gt;&amp;lt;ProgressBar style="@android:style/Widget.ProgressBar.Horizontal"&amp;gt;&lt;/pre&gt; which is like saying &lt;pre class="code"&gt;&amp;lt;ProgressBar &lt;br /&gt;    android:indeterminateOnly="false" &lt;br /&gt;    android:progressDrawable="@android:drawable/progress_horizontal" &lt;br /&gt;    android:indeterminateDrawable="@android:drawable/progress_indeterminate_horizontal" &lt;br /&gt;    android:minHeight="20dip" &lt;br /&gt;    android:maxHeight="20dip"&amp;gt;&lt;/pre&gt;Spiffy.&lt;/p&gt;&lt;p&gt;For bonus points, we can look in frameworks/base/core/res/res/drawable/progress_horizontal.xml. This file apparently contains the declarative expression of the horizontal scrollbar graphic. I don't know how extensive this syntax is, but a quick peek at progress_indeterminate_horizontal.xml indicates that it is possible to specify animations in this XML file format.&lt;/p&gt;&lt;p&gt;All this seems really powerful, only somewhat useful, and totally confusing. All I wanted was to create a horizontal progress bar. I can't tell why the Android widget collection includes only one ProgressBar class that is meant to be used for horizontal, indeterminate horizontal, indeterminate circular, and every other kind of possible progress indicator. Perhaps there would be duplication of code if those were separated into different classes. Whatever. I actually don't mind that they stuffed all those progress indicators into a single class. There are some questionable decisions (like having both indeterminate and indeterminateOnly), but I can deal with it. On the other hand, the incantations that you have to use to get the Progress Bar to behave are truly arcane. I think I have proven in this blog post that there is some method to the madness and, as is often the case, easy access to the source code is a must. What bothers me most is that there was absolutely no documentation on the subject. Google seems silent on this topic, and even The Busy Coder's Guide mentions that the style attribute won't be covered until a later edition of the book (though it is possible that the Google Books sample chapter is out of date at this point).&lt;/p&gt;&lt;p&gt;I still maintain that Android is one of the most unique application systems that I've seen. However, as I've tried to develop for it, I have encountered a general lack of polish. Many things that you want to do as a developer are confusing and poorly documented. Familiarity with existing technologies (such as Swing or Applet programming) is pretty much useless here. On the other hand, the core concepts (such as the use of Intents to start applications and communicate between processes, the HEAVY use of message passing, and the declarative nature of the AndroidManifest.xml file) seem to be an excellent base upon which to build not just an open operating system, but a truly open user experience. A power user can really replace virtually any aspect of the system. Perhaps developers will create third party libraries that make Android a little easier within which to develop, or maybe Google itself will provide simpler interfaces for developers. Or perhaps Android will die out because developers had a difficult time building anything particularly complex. Whatever the outcome, I'm excited for the ride.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-5000499182921486376?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/5000499182921486376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=5000499182921486376' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5000499182921486376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5000499182921486376'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/12/using-horizontal-progress-bar-in.html' title='Using a Horizontal Progress Bar in Android'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-7552544118910754569</id><published>2008-11-13T21:23:00.003-05:00</published><updated>2008-11-13T21:42:30.506-05:00</updated><title type='text'>Grep is fun</title><content type='html'>&lt;p&gt;I'm not what you would call a UNIX power user. I'm more of a UNIX casual user. I don't agree with the zealots who claim that the command line is king, and shell scripts keep the world spinning. It's a pain to remember the myriad of switches, and to remember the differences in those switches between platforms (BSD find requires a pathname, GNU find does not), etc. Maybe my distaste of the command line is why my primary computer is a Mac.&lt;/p&gt;&lt;p&gt;In any case, I was rooting around in the Android source code today, looking for examples involving the NotificationManager. I managed to whip up the following pipeline, of which I am proud.&lt;/p&gt;&lt;pre class="code"&gt;grep -rl --include=*.java NotificationManager . | xargs grep -l "\.clear" | xargs mate&lt;/pre&gt;&lt;p&gt;To break that down, &lt;div&gt;&lt;span class="code"&gt;grep -rl --include=*.java NotificationManager .&lt;/span&gt; This will recurse in the current directory, finding all .java files which contain the word NotificationManager. The resulting list of files will be passed to the next step.&lt;/div&gt;&lt;div&gt;&lt;span class="code"&gt;xargs grep -l "\.clear"&lt;/span&gt; This looks for the string ".clear" in any of the files that came from the first command. It outputs the list of matching files to the next command.&lt;/div&gt;&lt;div&gt;&lt;span class="code"&gt;xargs mate&lt;/span&gt; This launches TextMate, my editor of choice, with all of the files found in the previous step.&lt;/div&gt;&lt;/p&gt;&lt;p&gt;To summarize, this pipeline opens a text editor with every .java file in the android source code that contains both of the strings "NotificationManager" and ".clear". In my case, I was looking for all invocations of NotificationManager.clear(). Since the Android source code seems to be pretty consistent about explicitly importing all classes used, this scheme works pretty well.&lt;/p&gt;&lt;p&gt;If anybody knows of any command-line tools for searching a code base for Java-aware constructs (such as method definition, method invocation, etc.), please let me know! Something like that would be really, really handy. Eclipse provides all that, but it doesn't look trivial to get all of the Android source code into Eclipse projects.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-7552544118910754569?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/7552544118910754569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=7552544118910754569' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7552544118910754569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7552544118910754569'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/11/grep-is-fun.html' title='Grep is fun'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8631494649349999999</id><published>2008-11-11T00:49:00.004-05:00</published><updated>2009-08-07T13:36:25.793-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>AnyCut's "Make Your Own" Demystified</title><content type='html'>&lt;p&gt;I installed &lt;a href="http://code.google.com/p/apps-for-android/source/browse/trunk/AnyCut"&gt;AnyCut&lt;/a&gt; on my G1 a little while ago. AnyCut is a small app that allows you to create shortcuts to, well, almost anything. It has a mysterious "Make Your Own" option that isn't really documented at all. I started to play with it a bit, and it's not as bad as you would think.&lt;/p&gt;&lt;p&gt;The Make Your Own screen has three fields: Action, Data, and Type. I believe that these line up with the constructor parameters of &lt;a href="http://code.google.com/android/reference/android/content/Intent.html#Intent(java.lang.String,%20android.net.Uri,%20android.content.Context,%20java.lang.Class%3C?%3E)"&gt;public Intent(String action, Uri uri, Context packageContext, Class&amp;lt;?&amp;gt; cls)&lt;/a&gt;. The Action parameter is one of the string consts in the Intent class (such as android.intent.action.VIEW, not Intent.ACTION_VIEW), and the Data parameter should really be called Uri, because it includes the Uri of the item upon which you want to act. See also the &lt;a href="http://code.google.com/android/reference/available-intents.html"&gt;list of common intents&lt;/a&gt;. I imagine that the Type parameter is a fully-qualified class name, but it can be left blank for most uses. Finally, I suspect that the packageContext parameter is supplied by AnyCut itself.&lt;/p&gt;&lt;p&gt;I haven't yet had a chance to verify any of this against the source code (which I came across while writing this post), but it might be worth a look.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8631494649349999999?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8631494649349999999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8631494649349999999' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8631494649349999999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8631494649349999999'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/11/anycuts-make-your-own-demystified.html' title='AnyCut&apos;s &quot;Make Your Own&quot; Demystified'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-5708973671008368701</id><published>2008-10-30T21:11:00.002-04:00</published><updated>2008-10-30T21:18:13.371-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='googlegears'/><title type='text'>2 Days with Google Gears</title><content type='html'>&lt;p&gt;I had a chance to play with Google Gears on a mockup project recently. I was surprised to learn that my understanding of Gears was not the same as the reality of Gears. I had expected it to be a JS library to facilitate rich HTML applications to handle spotty network connections. It turned out that Gears is a browser plugin that adds useful tools that any rich web app developer would find useful, though they are all aligned to handle an app losing its network connection. In the two days that I spent playing with Gears, I was pretty much blown away by the power and simplicity of it.&lt;/p&gt;&lt;p&gt;Besides understanding what Gears is, it's important to also understand what Gears is not. Gears doesn't try to be a UI library, or a general "glue" library (see JQuery, Prototype, MochiKit, MooTools, or &lt;em&gt;any other&lt;/em&gt; Javascript framework). It doesn't really force you to code in any particular way (this is a bit of a lie, but more on that later). Just as a sampling, Gears includes:&lt;ul&gt;&lt;li&gt;A cross-browser compatible XMLHttpRequest&lt;/li&gt;&lt;li&gt;Local database storage&lt;/li&gt;&lt;li&gt;A web resource cache, so that items can still be fetched even if the network connection goes down. This is nearly completely transparent to the developer, and works for resources loaded by Gears or by the browser itself.&lt;/li&gt;&lt;li&gt;A mechanism to run scripts in the background (for real).&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;In our little demo, we have a set of checkboxes that can be checked. Initially, these would perform an asynchronous post to the server, where they would update some server-side state. However, if the app goes offline, nothing will ever reach the server. We modified this to enqueue changes into a local database, with a background script pulling items out of the database and sending them to the server. If the server ever goes down, that thread simply stops pulling items out of the database. In addition, we set our app up so that its resources (HTML, Javascript, CSS, and images) were initially cached when the app is first loaded. A neat feature of Gears seems to be that it will monitor the apps that it knows about and will automatically update its cache if the cache ever gets stale. Unfortunately, it's not perfect. It depends on the developer updating a version string, which causes Gears to update its cache from the source.&lt;/p&gt;&lt;p&gt;One problem that we had is that the HTML that we served up would include information about what items were checked. That is to say, when you would load the page, we would serve up some &amp;lt;input type="checkbox" /&amp;gt; and &amp;lt;input type="checkbox" checked="checked" /&amp;gt; elements. This makes total sense in a traditional web app. The client requests the page, and you serve it, and everybody is happy. Every time the page is served, it is reconstructed with the current state of the data. As you might imagine, this caused all kinds of problems for us. Concretely, we noticed that every time the page was reloaded (whether the network connection is up or down), the browser would display the state of the page as it was when the cache first acquired it. In a real application, that could mean that you are seeing data that is several months out of date. Now you see how I lied earlier. Gears does influence the way you code your application, but its requirements are about the same as those of any Javascript-heavy web app. As long as you separate your application presentation from your data, you should be fine.&lt;/p&gt;&lt;p&gt;Another thing that surprised and greatly pleased me was Gears' WorkerPool implementation. As everybody knows, it is impossible to run Javascript in the background in a normal web browser. I think that's because multi-threaded programming is hard, and Javascript can be pretty hairy as it is. I think that the browser designers have held off on implementing a threading solution out of fear that multithreaded Javascript would cause the apocalypse. As it turns out, though, Gears' implementation is both simple and powerful. Gears uses a message-passing mechanism for communication, with &lt;em&gt;absolutely no shared state&lt;/em&gt;. This is great news. As far as I can tell, just as your main JS code has an event loop, each worker also has an event loop. Whenever a message is sent from the main JS code to a worker, that message is copied and onMessage is invoked on by that worker's event loop. Likewise, when a worker sends a message back to the main JS, the message is copied and onMessage is invoked on the main event loop. This has some interesting implications. For one, none of the workers have access to the DOM, or to global variables defined on the page, and cannot participate in closures with mainline Javascript code. By placing a concrete wall between your page and your workers, Gears forces you to think about the interactions that the page and the worker will have, and that's a Good Thing. I'm sure that it's still possible for threading to ruin you, it's just a lot harder with a scheme like this.&lt;/p&gt;&lt;p&gt;And that's it. There's more to Gears that what I described (though not much more). It also includes some geolocation bits (presumably for Android, and maybe Safari Mobile, integration), desktop integration stuff, a standards-compliant timer, a file multi-chooser (yay!), and a binary data type (as opposed to String, which is for textual content). It's a shame that Gears is still in beta. I would really like to see some sites that use it. Of course, since I just recently installed Gears, there might be some sites that do and I never realized it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-5708973671008368701?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/5708973671008368701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=5708973671008368701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5708973671008368701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5708973671008368701'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/10/2-days-with-google-gears.html' title='2 Days with Google Gears'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6923801003258028327</id><published>2008-10-29T23:37:00.005-04:00</published><updated>2009-08-07T13:36:21.012-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>User-Visible Permissions in Android</title><content type='html'>&lt;p&gt;I picked up a &lt;a href="http://www.t-mobileg1.com/"&gt;T-Mobile G1 (danger: Flash-heavy site)&lt;/a&gt; at the local T-Mobile store. For those that don't know, the G1 is the first device to run &lt;a href="http://www.android.com/"&gt;Google's Android platform&lt;/a&gt;. So far I like it a lot, and I'll probably post a lot more about it in the near future.&lt;/p&gt;&lt;p&gt;Like the iPhone, Android has its own app store. Unlike the iPhone, nobody moderates apps submitted to the Android app store. If an app tries to do anything of consequence (i.e. anything that a user might want to know about), it must explicitly request that permission. When you start to download an app from the marketplace, it tells you what permissions that app will require. Most apps are well behaved, but some ask for way too much.&lt;/p&gt;&lt;p&gt;For example, I wanted a weather app. I saw that there is a Weather Channel app. When I went to download it, however, I was very surprised. Here is a list of the permissions that it requested.&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;Network communication&lt;/td&gt;&lt;td&gt;full Internet access&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Your location&lt;/td&gt;&lt;td&gt;coarse (network-based) location, fine (GPS) location&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;System tools&lt;/td&gt;&lt;td&gt;change network communication, change your UI settings, modify global system settings&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Your messages&lt;/td&gt;&lt;td&gt;edit SMS or MMS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Services that cost you money&lt;/td&gt;&lt;td&gt;send SMS messages&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Your personal information&lt;/td&gt;&lt;td&gt;read contact data&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;What? Why does this app need access to my contacts, or to send text messages? I hope that this was just a lazy developer who requested more permissions that he actually needed, but I'm suspicious. It's entirely possible that The Weather Channel intends to compile a list of all my contacts. Not cool. Especially since it makes no mention of that.&lt;/p&gt;&lt;p&gt;I think it's great that Android provides some ability for the end user to judge the software that they might install on their phone. I'll wait until The Weather Channel updates their app.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6923801003258028327?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6923801003258028327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6923801003258028327' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6923801003258028327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6923801003258028327'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/10/i-picked-up-t-mobile-g1-danger-flash.html' title='User-Visible Permissions in Android'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3702728359815416737</id><published>2008-10-14T01:01:00.003-04:00</published><updated>2008-10-14T01:20:41.042-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Scala's Model of Functions</title><content type='html'>&lt;p&gt;I was a little dismayed to learn that Scala models functions with different numbers of parameters  as instances of distinct function trait definitions. A practical upshot of this is that you can't really work with functions that take more than 22 parameters.&lt;/p&gt;&lt;pre class="code"&gt;def crazy(a:Int, b:Int, c:Int, d:Int, e:Int, f:Int, g:Int, h:Int, i:Int, j:Int, &lt;br /&gt;          k:Int, l:Int, m:Int, n:Int, o:Int, p:Int, q:Int, r:Int, s:Int, t:Int, &lt;br /&gt;          u:Int, v:Int) = 0&lt;br /&gt;(crazy _).curry    :    (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; &lt;br /&gt;                        (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; (Int) =&gt; &lt;br /&gt;                        (Int) =&gt; (Int) =&gt; Int = &amp;lt;function&amp;gt;&lt;br /&gt;&lt;br /&gt;def crazier(a:Int, b:Int, c:Int, d:Int, e:Int, f:Int, g:Int, h:Int, i:Int, j:Int, &lt;br /&gt;            k:Int, l:Int, m:Int, n:Int, o:Int, p:Int, q:Int, r:Int, s:Int, t:Int, &lt;br /&gt;            u:Int, v:Int, w:Int) = 0&lt;br /&gt;(crazier _).curry    :    &amp;lt;error&amp;gt;&lt;/pre&gt;&lt;p&gt;The scala runtime apparently has traits &lt;a href="http://www.scala-lang.org/docu/files/api/scala/Function0.html"&gt;Function0&lt;/a&gt; through &lt;a href="http://www.scala-lang.org/docu/files/api/scala/Function22.html"&gt;Function22&lt;/a&gt; defined. I guess this is so that they can have &lt;span class="method"&gt;call&lt;/span&gt; methods that take a statically known list of parameters (rather than, say, an array). That's all well and good, and probably necesary for proper Java interop, but it's still a little sad. Still, I don't expect to run into that limit any time soon. Oh wait, I have already worked on projects with functions that take more than 20 parameters. Maybe this was added just for me. Now I'm sad.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3702728359815416737?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3702728359815416737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3702728359815416737' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3702728359815416737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3702728359815416737'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/10/scalas-model-of-functions.html' title='Scala&apos;s Model of Functions'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1718163728178465368</id><published>2008-10-13T23:49:00.004-04:00</published><updated>2008-10-14T00:58:27.543-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Partial Application in Haskell and Scala</title><content type='html'>&lt;p&gt;This is an attempt to squeeze out a blog post while I wait for my laundry to finish.&lt;/p&gt;&lt;p&gt;Functional languages are fun. Fun in ways that Java (and, for that matter, Ruby) are not. Take Haskell. In that language, we can take any operator and turn it into a function. Normally, we use the symbol &lt;span class="operator"&gt;+&lt;/span&gt; to represent addition. If we enclose it in parentheses, we instead have a function.&lt;/p&gt;&lt;pre class="code"&gt;(+) :: (Num a) =&gt; a -&gt; a -&gt; a&lt;/pre&gt;&lt;p&gt;In this case, &lt;span class="expression"&gt;(+)&lt;/span&gt; is a function of 2 number parameters, which returns a number. Now that we have a function, we can apply all of the standard Haskell magic to it. Since Haskell is automatically curried (no function really ever takes more than one parameter), we chain calls to fully evaluate our (+) function.&lt;/p&gt;&lt;pre class="code"&gt;(+) 2 3 =&gt; 5&lt;/pre&gt;&lt;p&gt;We can also partially apply this operator.&lt;/p&gt;&lt;pre class="code"&gt;add5 :: Integer -&gt; Integer&lt;br /&gt;add5 = (+) 5&lt;br /&gt;add5 3 =&gt; 8&lt;/pre&gt;&lt;p&gt;In this case, we have created an alias for the partially bound &lt;span class="operator"&gt;+&lt;/span&gt; operator. Rather than jump through so many hoops, we could specify add5 more directly.&lt;/p&gt;&lt;pre class="code"&gt;add5 = (5+)&lt;/pre&gt;&lt;p&gt;Finally, a slightly more complicated example.&lt;/p&gt;&lt;pre class="code"&gt;simple :: Integer -&gt; Integer -&gt; Integer -&gt; Integer&lt;br /&gt;simple x y z = x * (y + z)&lt;br /&gt;&lt;br /&gt;simpler :: Integer -&gt; Integer -&gt; Integer&lt;br /&gt;simpler = simple 2&lt;br /&gt;&lt;br /&gt;simplest :: Integer&lt;br /&gt;simplest = simpler 3 4 =&gt; 14&lt;/pre&gt;&lt;p&gt;All functions are also values in Haskell.&lt;/p&gt;&lt;pre class="code"&gt;easy = simple&lt;br /&gt;easy 2 3 4 =&gt; 14&lt;/pre&gt;&lt;p&gt;As you can see, in Haskell, we can turn any operator into a function. Functions are curried, and can be partially evaluated from the left. Functions are also values that can be assigned and passed around as needed.&lt;/p&gt;&lt;p&gt;Scala takes a different approach. In Scala, operators are actually methods on values. There is no global + operator. Instead, you invoke the + method on the left hand parameter.&lt;/p&gt;&lt;pre class="code"&gt;5 + 3 //is the same as...&lt;br /&gt;(5).+(3)&lt;/pre&gt;&lt;p&gt;If you want to refer to a function as a value in Scala, you must "partially apply" it to zero parameters.&lt;/p&gt;&lt;pre class="code"&gt;val output = println //will result in a compilation error&lt;br /&gt;val output = println _&lt;br /&gt;output "Oh Hai, World!"&lt;/pre&gt;&lt;p&gt;The underscore is the Scala placeholder operator. If used as we did with println, it stands in for the whole argument list, effectively turning the function into a function value. It is also the mechanism by which we can partially apply a function.&lt;/p&gt;&lt;pre class="code"&gt;def simple(x:Int, y:Int, z:Int) = x * (y + z)&lt;br /&gt;val simpler = simple(2, _:Int, _:Int)&lt;br /&gt;simpler(3, 4) =&gt; 14&lt;/pre&gt;&lt;p&gt;The underscores, when used this way, compel the result of the expression to itself be a function that takes n parameters, where n is the number of placeholders. Sometimes, it is possible to infer the type of the missing parameters; other times, it isn't. It depends on how the parameters are used.&lt;/p&gt;&lt;p&gt;It is very important to notice that, unlike Haskell, it is very easy to bind only the parameter in the middle of this expression.&lt;/p&gt;&lt;pre class="code"&gt;val sample = simple(_:Int, 3, _:Int)&lt;br /&gt;sample(2, 4) =&gt; 14&lt;/pre&gt;&lt;p&gt;By combining placeholder syntax with operators, it is possible to turn an operator into a function, even a function that takes its left operand as a parameter.&lt;/p&gt;&lt;pre class="code"&gt;List(1, 2, 3).map(_ + 2) =&gt; List(3, 4, 5)&lt;br /&gt;List(1, 2, 3).reduceLeft(_ + _) =&gt; 6&lt;/pre&gt;&lt;p&gt;As you can see, Haskell and Scala have a lot in common. Haskell's syntax is a bit more concise (and its inference rules much better), but Scala's ability to bind any parameter is pretty handy, too. There's something both cluttered and clean about Haskell's use of underscores, especially when types aren't required. Of course, I'm not an expert (or, in fact, experienced at all) with either language, so please correct me if I got any of my facts wrong.&lt;/p&gt;&lt;p&gt;Looks like I failed. My laundry was done 30 minutes ago.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-1718163728178465368?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1718163728178465368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1718163728178465368' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1718163728178465368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1718163728178465368'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/10/partial-application-in-haskell-and.html' title='Partial Application in Haskell and Scala'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4053702744368396102</id><published>2008-10-04T10:43:00.003-04:00</published><updated>2008-10-04T11:25:06.380-04:00</updated><title type='text'>The Machine</title><content type='html'>&lt;p&gt;Two weeks ago, I had the opportunity to see &lt;a href="http://www.themachinelive.com/"&gt;The Machine&lt;/a&gt; with my family. The Machine is a Pink Floyd tribute band. That is to say, at their shows, they play nothing but Pink Floyd music. All of the musicians are clearly extreme Floyd fans. I mean, why else would you spend 20 years of your life playing somebody else's music? Now, some people don't like tribute bands. I had a hard time getting people to see &lt;a href="http://www.aussiefloyd.com/"&gt;The Australian Pink Floyd Show&lt;/a&gt; when they came to New York (playing literally a few blocks from where we were staying). Who cares that these aren't the original musicians? Would you also refuse to go to a performance of Beethoven's 5th because it wasn't being conducted by the man himself? Of course not! The music is just as good, and the musicians are going to make it special and awesome anyway. But I digress...&lt;/p&gt;&lt;p&gt; It was interesting to see the variety in people in the theater. Obviously, many of the patrons were my parents' age, but there were also some college kids and folks whose heads were completely gray. What was perhaps more interesting to me is that the 50 year olds were more animated and crazy than the college kids. They had some smoke machines up on stage, but I don't think that was the source of all the smoke in the hall. It's fun to watch adults relive their youth.&lt;/p&gt;&lt;p&gt;The set was Dark Side of the Moon (with the Wizard of Oz projected onto their own version of &lt;a href="http://en.wikipedia.org/wiki/Pink_Floyd_live_performances#The_light_show"&gt;Mr. Screen&lt;/a&gt;), followed by an intermission, followed by The Wall. Not a bad setlist at all. As they launched into the beginning songs from Dark Side, I was carefully listening for any variation from the album tracks that I know so well. I couldn't help it. These guys were playing well-known and well-loved music, so it's only natural to compare their performance to the original. By The Great Gig in the Sky, though, I was totally sold. The woman that belted out those notes was simply amazing. She absolutely hit every note. It was surreal. The keyboardist was younger than the rest and totally crazy, with a maniacal grin that was somehow larger than his actual face. The drummer hid behind the drums for most of the show, but did a very good job. The bassist seemed detached, standing apart from the others. I suspect that was &lt;a href="http://en.wikipedia.org/wiki/Roger_Waters#Pink_Floyd_years_.281965.E2.80.931985.29"&gt;completely intentional&lt;/a&gt;. The saxophone player was decent, but wasn't very memorable (after all, he only played on a few songs). Rounding out the group is the lead guitarist / lead singer. His ability to mimic both David Gilmour &lt;em&gt;and&lt;/em&gt; Roger Waters was spooky. The man knew his guitar well, and made it sound just like the original.&lt;/p&gt;&lt;p&gt;By the time they were playing The Wall, people in the crowd were singing along. Performing Dark Side first was a good idea. People were more mellow when the entered the theater than when they left, and Dark Side is best appreciated without whoops and cheers. The Wall, on the other hand, is great with audience participation. In the end, they ended up getting 4 standing ovations (after Dark Side, after (I think) Comfortably Numb, after The Wall, and after their encore of Run Like Hell). They deserved each and every one of them. They probably played for 2.5 hours all told.&lt;/p&gt;&lt;p&gt;I never got a chance to see Pink Floyd live. As one of the people sitting next to us pointed it, this is the closest you can get at this point. While I agree with him, it is wrong to think of these guys as a facsimile of that famous band. These are all very talented musicians who love this music so much that they have dedicated a big chunk of their lives to it. As a fan, I'm grateful to them for doing that.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4053702744368396102?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4053702744368396102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4053702744368396102' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4053702744368396102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4053702744368396102'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/10/machine.html' title='The Machine'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6766748432160464515</id><published>2008-09-06T01:30:00.007-04:00</published><updated>2008-10-14T18:42:54.624-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>No More Statics! Part 2</title><content type='html'>&lt;p&gt;In a &lt;a href="http://bytecrafter.blogspot.com/2008/09/no-more-statics.html"&gt;previous post&lt;/a&gt;, I explained how Scala's use of singleton objects is better than Java's use of static members. I was asked for some sample code after that post, so I thought I would throw some together. Let's look at a simple Java class.&lt;/p&gt;&lt;pre class="code"&gt;class Foo {&lt;br /&gt;    private static int number = 1;&lt;br /&gt;    &lt;br /&gt;    public static Foo create(String a) {&lt;br /&gt;        return new Foo("Some " + a);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    private String a;&lt;br /&gt;    private int id = number++;&lt;br /&gt;    &lt;br /&gt;    public Foo(String a) {&lt;br /&gt;        this.a = a;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    @Override&lt;br /&gt;    public String toString() {&lt;br /&gt;        return "Foo #" + id + " is " + a;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This class keeps track of how many instances have ever been created. You construct a Foo with a name, and the Foo's name and id are part of its string representation. In addition, there is a create method that has been defined on the Foo class itself.&lt;/p&gt;&lt;p&gt;Scala doesn't have a "static" keyword. Instead, members that would otherwise be static are placed onto the so-called companion object. &lt;/p&gt;&lt;pre class="code"&gt;object Foo {&lt;br /&gt;  var number:Int = 1;&lt;br /&gt;&lt;br /&gt;  def create(a:String) = new Foo("Some " + a)&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Foo(a:String) {&lt;br /&gt;  private val id:Int = Foo.number&lt;br /&gt;  Foo.number = Foo.number + 1&lt;br /&gt;&lt;br /&gt;  override def toString() = {&lt;br /&gt;    "Foo #" + id + " is " + a&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Because Foo is the name of both an object and a class in the same package, they are allowed to access each other's private members. Basically, this makes the instance members of a singleton object equivalent to static members of an ordinary Java class. However, since the singleton object is a fully fledged object, it can be passed around in a way that Java classes normally can't be.&lt;/p&gt;&lt;pre class="code"&gt;def createList(f : Foo.type) = {&lt;br /&gt;  List(f.create("One"), f.create("Two"))&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Have you ever wanted a Java class' static members to obey an interface? Well, the singleton object can mix in Scala traits (Scala traits seem to take the place of both interfaces and mixins from other languages).&lt;/p&gt;&lt;pre class="code"&gt;trait Creatable[A] {&lt;br /&gt;  def create(a:String) : A&lt;br /&gt;&lt;br /&gt;  def createDefault() : A = {&lt;br /&gt;    return create("Default")&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;object Foo extends Creatable[Foo] {&lt;br /&gt;  var number:Int = 1;&lt;br /&gt;&lt;br /&gt;  override def create(a:String) = new Foo(a)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;And here's a whole sample program:&lt;/p&gt;&lt;pre class="code"&gt;trait Creatable[A] {&lt;br /&gt;  def create(a:String) : A&lt;br /&gt;  &lt;br /&gt;  def createDefault() : A = {&lt;br /&gt;    return create("Default")&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;object Foo extends Creatable[Foo] {&lt;br /&gt;  var number:Int = 1;&lt;br /&gt;  &lt;br /&gt;  override def create(a:String) = new Foo(a)&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Foo(a:String) {&lt;br /&gt;  private val id:Int = Foo.number&lt;br /&gt;  Foo.number = Foo.number + 1&lt;br /&gt;&lt;br /&gt;  override def toString() = {&lt;br /&gt;    "Foo #" + id + " is " + a&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def createList(f : Creatable[Foo]) = {&lt;br /&gt;  List(f.create("Three"), f.create("Four"))&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;println(Foo.create("One"))&lt;br /&gt;println(Foo.create("Two"))&lt;br /&gt;println(createList(Foo))&lt;br /&gt;println(Foo.createDefault())&lt;br /&gt;&lt;br /&gt;----------&lt;br /&gt;&lt;br /&gt;Foo #1 is One&lt;br /&gt;Foo #2 is Two&lt;br /&gt;List(Foo #3 is Three, Foo #4 is Four)&lt;br /&gt;Foo #5 is Default&lt;/pre&gt;&lt;p&gt;Why are singleton objects better than static members? To begin with, Scala's singleton objects are at least as expressive as static class members, so you're not losing anything from Java. You define a singleton object differently that you define static members in Java, but you access them using notation identical to Java (i.e. Foo.bar(5) in both languages). In addition, you get some other nice features - first class object status and the ability to participate in the normal class hierarchy. As an added bonus, Scala's simpler syntax actually made the class/singleton-object pair &lt;em&gt;shorter&lt;/em&gt; than the equivalent Java solution. Not bad!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6766748432160464515?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6766748432160464515/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6766748432160464515' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6766748432160464515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6766748432160464515'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/09/no-more-statics-part-2.html' title='No More Statics! Part 2'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-7214364505595541323</id><published>2008-09-02T19:30:00.003-04:00</published><updated>2008-09-02T20:08:21.045-04:00</updated><title type='text'>On Architecture</title><content type='html'>&lt;p&gt;The other day, a coworker told me that they want to improve their architectural skills. That brought up a number of questions for me. What is architecture and how does it differ from programming? What are traits of a good architecture? What about a good architect? Am I a good architect?&lt;/p&gt;&lt;p&gt;"Traits of a good architecture" is something that gets covered in a lot of CS textbooks: extensibility, security, resilience, and so on. However, concentrating on those traits won't make you a better architect.&lt;/p&gt;&lt;p&gt;I don't know what will improve your architectural skills, but I do know what I think about when I'm working on architecture. I don't necessarily architect by reason. I have "good code" values, and I evaluate all the code that I write or encounter against them. For example, I think that exceptions (if available) should be used to signal the presence of and to recover from erroneous conditions. Status codes are straight out, and "fixups"&lt;a href="#footnote-1"&gt;[1]&lt;/a&gt; are a bad idea, too. Only in rare performance-critical areas are other mechanisms appropriate. &lt;/p&gt;&lt;p&gt;Where did I get my "good code" values? I've built them up from my personal and professional experience. I've been writing toy programs for 18 years. A lot of that is useless today, but some of those experiences have taught me valuable lessons. Just like a master carpenter didn't acquire his skills overnight, a software architect needs to develop his skills over a period of time. Trying, failing, reading, watching, discussing, and reflecting are all useful for developing this sense.&lt;/p&gt;&lt;p&gt;Just like other developers, I like to program. In fact, for an architect, it's essential to get one's hands dirty. However, it's also important to know when to step back and look at the big picture. For me, it's all about relying on that design "sense". When people talk about code smells, it's because something smells rank to their design sense. On the other hand, it's important not to get paralyzed by that sense. If you're unsure what direction you should be heading, it might be time to try something. Commit your current work (or, if using Git or Mercurial, create a new branch) and try going down a path. Keep your eyes open along the way, and learn what works and what does not. If you have time, try the other path. It might also make sense to start a new "toy" project to try some ideas without being burdened by the current code base. Also, be ready to revert all of your changes. Most of the work in programming is wrapped up in the thinking. Once you know how to do something, it should be reasonably simple to reproduce your work&lt;a href="#footnote-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Finally, I'm a big fan of deciding on a direction and pursuing it. Sometimes, you don't know what to do, and it's too much work to mock up or try out even one approach. In this case, I ask myself how I would want it to work, and I try to do that. I ignore technical constraints unless they are insurmountable. Blindly following a path because it is "right" can get you into a lot of trouble, but so can meandering about without any goal in sight.&lt;/p&gt;&lt;p&gt;What do other people think? What is architecture to you, and how do you improve those skills?&lt;/p&gt;&lt;p&gt;&lt;a name="footnote-1"&gt;[1]&lt;/a&gt; In this case, I'm talking about code which detects invalid input or state (which is a good thing), but then changes the input or state to be valid. An example would be a function with a parameter whose value needs to be in the range [1, 1000], but which will assume 0 for any invalid value. This makes the function always terminate normally (after all, there are no longer any invalid inputs), but it doesn't necessarily ensure correctness of the program. Who's to say that 24124142 and 0 are equivalent, anyway? Exceptions are better because they allow the caller, not the callee, to determine the result in the case of an invalid condition.&lt;/p&gt;&lt;p&gt;&lt;a name="footnote-2"&gt;[2]&lt;/a&gt; If, on the other hand, most of your programming effort is spent on pushing keys on a keyboard, you're in a difficult position. Programming is primarily a thinking man's game, not a typing man's game. Your goal should not be to generate the most code in a day, but rather to generate the most value per line of code. Partially, then, your goal should be to keep the number of lines of code to a minimum. Removing code while retaining functionality, flexibility, and performance is always a good thing.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-7214364505595541323?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/7214364505595541323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=7214364505595541323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7214364505595541323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7214364505595541323'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/09/on-architecture.html' title='On Architecture'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8718528319070715892</id><published>2008-09-02T19:00:00.005-04:00</published><updated>2008-10-04T11:28:44.794-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>No More Statics!</title><content type='html'>&lt;p&gt;As I read more about Scala, I'm running across a lot of things that I like. In Scala, there are no static members: no static methods; no static fields. Instead, Scala has so-called "singleton" objects. These singleton objects are globally accessible, though their instance methods and fields are still subject to access restriction. This is great because it exposes what we all knew all along: that static fields and methods in Java are really just global variables and functions. Granted, they are access-controlled, namespaced globals, but they're still globals.&lt;/p&gt;&lt;p&gt;Since each class' singleton object is in fact an object, it can subclass another object or mix in traits, just like objects that are spawned by a class. The singleton object has the same rights as any other object in the system.&lt;/p&gt;&lt;p&gt;In addition, a singleton object can share a name with a class; if it does so, they can access each other's private data. I'm not sure yet, but I assume that this is how Scala accesses static members of Java classes - it creates a singleton object that doesn't derive or mix in anything, but turns all the static methods and fields of the Java class into instance members of the singleton object.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8718528319070715892?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8718528319070715892/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8718528319070715892' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8718528319070715892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8718528319070715892'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/09/no-more-statics.html' title='No More Statics!'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1588880529579517538</id><published>2008-08-31T22:43:00.005-04:00</published><updated>2008-10-04T11:28:41.092-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Terseness for Terseness' Sake</title><content type='html'>&lt;p&gt;I've been reading up on Scala, since it seems like it may be a better Java than Java itself. As I was reading through the pre-release PDF of &lt;a href="http://www.artima.com/shop/programming_in_scala" class="book-title"&gt;Programming in Scala&lt;/a&gt;, I came across something goofy.&lt;/p&gt;&lt;p&gt;Scala, like (as I understand it) F#, tries to nestle itself comfortably between the functional and imperative camps. It has a syntax that supports both schools of though. So, as you might expect, some functions in Scala will behave nicely and will return a value without any side effects. Other functions will be executed solely for the side effects, and will return nothing (the Unit type in Scala). To further the Functional mindset, Scala does not require an explicit return statement at the end of a function. Instead, the last value in the function is used as the value of the function. &lt;span class="book-title"&gt;Programming in Scala&lt;/span&gt; is quick to point out that, if you want, you can just as easily use explicit return statements (if that floats your boat).&lt;/p&gt;&lt;p&gt;The functional and imperative worlds collide in a shower of fireworks. From &lt;span class="book-title"&gt;Programming in Scala&lt;/span&gt;:&lt;blockquote&gt;One puzzler to watch out for is that whenever you leave off the equals sign before the body of a function, its result type will definitely be Unit. This is true no matter what the body contains, because the Scala compiler can convert any type to Unit. For example, if the last result of a method is a String, but the method’s result type is declared to be Unit, the String will be converted to Unit and its value lost.&lt;/blockquote&gt;The book then goes on to provide an example where a function's value is accidentally lost.&lt;/p&gt;&lt;p&gt;Now, I'm all for shortening my programs. The less I have to type, the better. This is, in fact, one of the big advantages Scala has over Java. But wait just a minute! I thought that our compilers were supposed to help us, not trip us up! Here's a situation where 2 different things (a function's return value and the function's return statement) are optional. If they are not specified, they are inferred. In that case, the only difference between retaining and losing your return value is a single character - a '='.&lt;/p&gt;&lt;p&gt;To get all concrete, here are a pair of Scala programs that do different things. &lt;pre class="code"&gt;package org.balefrost.demo&lt;br /&gt;&lt;br /&gt;object Sample {&lt;br /&gt;  def foo {&lt;br /&gt;    "bar"&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  def main(args : Array[String]) : Unit = {&lt;br /&gt;    val baz = foo&lt;br /&gt;    println(baz)&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="result"&gt;=&gt; ()&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;pre class="code"&gt;package org.balefrost.demo&lt;br /&gt;&lt;br /&gt;object Sample {&lt;br /&gt;  def foo &lt;span class="changed"&gt;= &lt;/span&gt;{&lt;br /&gt;    "bar"&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  def main(args : Array[String]) : Unit = {&lt;br /&gt;    val baz = foo&lt;br /&gt;    println(baz)&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="result"&gt;=&gt; "bar"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;I don't know. To me, that's goofy. Other people might find it completely reasonable. Of course, you can protect yourself with explicit types.&lt;pre class="code"&gt;package org.balefrost.demo&lt;br /&gt;&lt;br /&gt;object Sample {&lt;br /&gt;  def foo {&lt;br /&gt;    "bar"&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  def main(args : Array[String]) : Unit = {&lt;br /&gt;    val baz&lt;span class="changed"&gt;:String&lt;/span&gt; = foo  //compiler error: can't assign Unit to String&lt;br /&gt;    println(baz)&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Anyway, kudos to &lt;span class="book-title"&gt;Programming in Scala&lt;/span&gt; for pointing out the potential cause of a hair-yankingly-frustrating bug. Now that I understand what's going on, I will probably be better able to handle it when it comes up in a real program.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-1588880529579517538?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1588880529579517538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1588880529579517538' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1588880529579517538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1588880529579517538'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/08/terseness-for-terseness-sake.html' title='Terseness for Terseness&apos; Sake'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-2020728605522424718</id><published>2008-08-14T22:48:00.005-04:00</published><updated>2008-08-14T22:56:31.205-04:00</updated><title type='text'>Should I Squash Underhanded Corporate Comments or Let Them Live?</title><content type='html'>&lt;p&gt;At this point, my &lt;a href="http://bytecrafter.blogspot.com/2008/02/memeo-autosync-is-extremely-slow.html"&gt;Memeo Autosync post&lt;/a&gt; has gotten a &lt;a href="http://bytecrafter.blogspot.com/2008/02/memeo-autosync-is-extremely-slow.html?showComment=1217445240000#c4074467409652368417"&gt;few&lt;/a&gt; &lt;a href="http://bytecrafter.blogspot.com/2008/02/memeo-autosync-is-extremely-slow.html?showComment=1217446920000#c7130287144496891357"&gt;comments&lt;/a&gt; that clearly originate from somebody who works for(or otherwise has a stake in) Memeo. On one hand, I really dislike this corporate intrusion in an otherwise pristine blog. They have masqueraded as a genuine user, which is misleading and underhanded. On the other hand, it appears that they have offered a discount on Memeo software.&lt;/p&gt;&lt;p&gt;What do other bloggers do with these situations? Do they squash comments that are subversive like this? Do they just allow them, realizing that blog readers are intelligent individuals and will notice the obvious deception? What do you think?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-2020728605522424718?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/2020728605522424718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=2020728605522424718' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2020728605522424718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2020728605522424718'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/08/should-i-squash-underhanded-corporate.html' title='Should I Squash Underhanded Corporate Comments or Let Them Live?'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-9009627875313537489</id><published>2008-08-14T21:40:00.005-04:00</published><updated>2008-08-14T23:09:02.449-04:00</updated><title type='text'>Why You Can Throw Away "If", and Why You Shouldn't</title><content type='html'>&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;Most reasonably experienced object-oriented programmers have probably stumbled upon the same realization; namely, that it's possible to replace &lt;span class="keyword"&gt;if&lt;/span&gt; statements with polymorphism. Polymorphism is simply a way to delay a decision until runtime. The &lt;span class="keyword"&gt;if&lt;/span&gt; statement does the same thing. In fact, procedural programmers need to resort to things like &lt;span class="keyword"&gt;if&lt;/span&gt; and &lt;span class="keyword"&gt;switch&lt;/span&gt; statements because they have no other tool. Functional programmers, on the other hand, simply toss functions around willy nilly.&lt;/p&gt;&lt;p&gt;This realization can be powerful. It can also really hurt a code base (I know - I've smashed my share of algorithmic china with this hammer). I recently ran into a place on a project where it was a great idea, and I thought I would share why I thought it worked so well.&lt;/p&gt;&lt;h4&gt;Current Implementation&lt;/h4&gt;&lt;p&gt;Suppose you have 2 methods:&lt;pre class="code"&gt;public void storeNewItem() {&lt;br /&gt;    Item item = new Item();&lt;br /&gt;    item.name = request["name"];&lt;br /&gt;    item.description = request["description"];&lt;br /&gt;    item.quantity = request["quantity"];&lt;br /&gt;    item.value = someComplexCalculation();&lt;br /&gt;    item.totalValue = item.quantity * item.value;&lt;br /&gt;    // calculate and store some more fields here&lt;br /&gt;    items.addNewItem(item);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void storeExistingItem() {&lt;br /&gt;    Item item = items.get(request["itemId"]);&lt;br /&gt;    item.name = request["name"];&lt;br /&gt;    item.description = request["description"];&lt;br /&gt;    item.quantity = request["quantity"];&lt;br /&gt;    item.value = someComplexCalculation();&lt;br /&gt;    item.totalValue = item.quantity * item.value;&lt;br /&gt;    // calculate and store some more fields here&lt;br /&gt;    item.update();&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;These two functions should look pretty similar. In fact, they are nearly identical. Both acquire an item, populate it with data, and then store it. The only difference is the way that the item is acquired and the way that the item is stored. &lt;/p&gt;&lt;h4&gt;First Attempt&lt;/h4&gt;&lt;p&gt;I wanted to merge these methods, and this was my first attempt.&lt;pre class="code"&gt;public void storeItem() {&lt;br /&gt;    Item item;&lt;br /&gt;    if (request["itemId"] == null) {&lt;br /&gt;        item = new Item();&lt;br /&gt;    } else {&lt;br /&gt;        item = items.get(request["itemId"]);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    item.name = request["name"];&lt;br /&gt;    item.description = request["description"];&lt;br /&gt;    item.quantity = request["quantity"];&lt;br /&gt;    item.value = someComplexCalculation();&lt;br /&gt;    item.totalValue = item.quantity * item.value;&lt;br /&gt;    // calculate and store some more fields here&lt;br /&gt;&lt;br /&gt;    if (request["itemId"] == null) {&lt;br /&gt;        items.addNewItem(item);&lt;br /&gt;    } else {&lt;br /&gt;        item.update();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;This works, but is obvious crap. I found myself saying "I wish Item were able to handle those details by itself".&lt;/p&gt;&lt;h4&gt;Second Attempt&lt;/h4&gt;&lt;p&gt;Well, I wasn't brave enough to change Item, so I instead wrapped it.&lt;pre class="code"&gt;public void storeItem() {&lt;br /&gt;    Persister persister;&lt;br /&gt;    if (request["itemId"] == null) {&lt;br /&gt;        persister = new NewItemPersister();&lt;br /&gt;    } else {&lt;br /&gt;        persister = new ExistingItemPersister(request["itemId"]);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    Item item = persister.getItem();&lt;br /&gt;    item.name = request["name"];&lt;br /&gt;    item.description = request["description"];&lt;br /&gt;    item.quantity = request["quantity"];&lt;br /&gt;    item.value = someComplexCalculation();&lt;br /&gt;    item.totalValue = item.quantity * item.value;&lt;br /&gt;    // calculate and store some more fields here&lt;br /&gt;    persister.persist();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;interface Persister {&lt;br /&gt;    Item getItem();&lt;br /&gt;    void persist();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class NewItemPersister implements Persister {&lt;br /&gt;    private Item item = new Item();&lt;br /&gt;    &lt;br /&gt;    public Item getItem() { return item; }&lt;br /&gt;    &lt;br /&gt;    public void persist() { items.addNewItem(item); }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class ExistingItemPersister implements Persister {&lt;br /&gt;    private Item item;&lt;br /&gt;    &lt;br /&gt;    public ExistingItemPersister(String itemId) {&lt;br /&gt;        item = items.get(request["itemId"]);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public Item getItem() { return item; }&lt;br /&gt;    &lt;br /&gt;    public void persist() { item.update(); }&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;We still have an ugly &lt;span class="keyword"&gt;if&lt;/span&gt; at the top of the function, and we have certainly ballooned the code. I still think that this is better than what we started with. &lt;ul&gt;&lt;li&gt;There is less duplication, which will make maintenance here much easier.&lt;/li&gt;&lt;li&gt;The Persister interface could be made into a generic class, and the implementations could be re-used all over the system. Some reflection here could really simplify your life.&lt;/li&gt;&lt;li&gt;A good web framework would allow you to remove that pesky initial if statement. In a less good framework, you could hide this behind some sort of object that knows how to generate a persister from an itemId (or null).&lt;/li&gt;&lt;/ul&gt;The practical upshot is that these changes should make it easier to apply metaprogramming techniques to this chunk of code. The only code that can't really be made declarative is some of the code which assigns values to fields.&lt;/p&gt;&lt;p&gt;There is one thing that bothers me, though. We have made the Persister implementors responsible for the lifetime of the Item. That's not at all clear from the interface, but it is obvious from the use. The tell-tale sign is that we have a getItem() method. Getters that expose their class' internals like this are evil, and if you don't believe me, you're just plain wrong. I won't try to justify that statement in this post, but trust me.&lt;/p&gt;&lt;h4&gt;Third Attempt&lt;/h4&gt;&lt;p&gt;To solve this, we could change the interface yet again (and I will switch to Javascript, because Java doesn't have any convenient lambda syntax).&lt;pre class="code"&gt;function storeItem() {&lt;br /&gt;    if (request["itemId"] == null) {&lt;br /&gt;        var persister = newItemPersister;&lt;br /&gt;    } else {&lt;br /&gt;        var persister = new ExistingItemPersister(request["itemId"]);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    persister.update(function(item) {&lt;br /&gt;        item.name = request["name"];&lt;br /&gt;        item.description = request["description"];&lt;br /&gt;        item.quantity = request["quantity"];&lt;br /&gt;        item.value = someComplexCalculation();&lt;br /&gt;        item.totalValue = item.quantity * item.value;&lt;br /&gt;        // calculate and store some more fields here&lt;br /&gt;    });&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var newItemPersister {&lt;br /&gt;    update:function(f) {&lt;br /&gt;        var item = new Item();&lt;br /&gt;        f(item);&lt;br /&gt;        items.addNewItem(item);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function ExistingItemPersister(itemId) {&lt;br /&gt;    this.itemId = itemId;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;ExistingItemPersister.prototype.update = function(f) {&lt;br /&gt;    var item = items.get(request["itemId"]);&lt;br /&gt;    f(item);&lt;br /&gt;    item.update();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Now, the item's lifetime is only as long as a call to &lt;span class="method"&gt;update()&lt;/span&gt; is on the stack. This is a common idiom in Ruby, as well.&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;In the end, I wasn't completely happy with any of these solutions. I think that things are better than they were before. There are also a number of other permutations that will get it marginally closer to ideal. I think that the real solution is to update Item so that you can create a new item and save an existing item with a single method. After that, the code to choose whether to create a new object or fetch an existing object should be minimal and extractable.&lt;/p&gt;&lt;p&gt;I did learn a rule of thumb for deciding when to replace an if statement with polymorphism. If you find yourself saying "I don't want to deal with this, I wish it were handled for me," there's a good chance that you could benefit from some polymorphism. Also, if you find yourself checking the same condition multiple times in a function (as we had in the original implementation), you might want to consider whether polymorphism will help you.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-9009627875313537489?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/9009627875313537489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=9009627875313537489' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/9009627875313537489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/9009627875313537489'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/08/why-you-can-throw-away-if-and-why-you.html' title='Why You Can Throw Away &quot;If&quot;, and Why You Shouldn&apos;t'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-5002587506959127680</id><published>2008-08-07T00:53:00.006-04:00</published><updated>2008-08-07T02:45:42.457-04:00</updated><title type='text'>Experiments in Firmware Hacking</title><content type='html'>&lt;p&gt;I got a new ethernet-ready printer today, and wanted to add it to my existing wireless network. This is not the usual home wireless network use case - most people want to share an upstream connection with a bunch of wireless clients. I wanted to connect a wired device to a wireless network. I first tried using a spare Airport Express. That worked perfectly. Then, I decided to try getting my &lt;a href="http://www.linksys.com/servlet/Satellite?c=L_Product_C2&amp;amp;childpagename=US%2FLayout&amp;amp;cid=1149562300349&amp;amp;pagename=Linksys%2FCommon%2FVisitorWrapper"&gt;Linksys WRT54G&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/WRT54G#WRT54G"&gt;v2&lt;/a&gt; to work. When I realized that the stock firmware was definitely not up to the task, I grabbed &lt;a href="http://www.polarcloud.com/tomato"&gt;Tomato&lt;/a&gt;. It claims to be solid, fast, and AJAX-y with realtime, SVG charts. How could I resist. However, the settings that I needed weren't obvious at first. After some fiddling, I think I've made it work. I'll share them here in case they're useful to somebody.&lt;/p&gt;&lt;p&gt;The most important setting is Wireless Mode (under Basic/Network). Here's my current best understanding of these modes:&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Access Point&lt;/td&gt;&lt;td&gt;This is what the Linksys router would do with the default firmware. It allows wireless clients to connect to it in infrastructure mode, and will route packets between the wireless network, the LAN network, and the WAN network (with NAT).&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Access Point + WDS&lt;/td&gt;&lt;td&gt;I think this may work like the Airport Express' WDS Remote mode. That would mean that it can accept wireless clients and simultaneously connect to a WDS network.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Wireless Client&lt;/td&gt;&lt;td&gt;This mode appeared to work like the Wireless Ethernet Bridge mode, except &lt;em&gt;with&lt;/em&gt; NAT. It appeared that the router will can run a DHCP server on the LAN interfaces. It also requires that the WAN port be configured, which seems very strange to me.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="font-weight:bold;"&gt;Wireless Ethernet Bridge&lt;/td&gt;&lt;td&gt;This is the one that ended up doing what I need. As far as I can tell, the WAN port is disabled. The device connects to an existing wireless network. It will then route packets between the wired and wireless network &lt;em&gt;without&lt;/em&gt; NAT. Furthermore, contrary to other reports, it appears that you can connect devices to more than one of the LAN ports. I had both my printer and my laptop connected to LAN ports, and things still seemed to be working.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WDS&lt;/td&gt;&lt;td&gt;I think this may be similar to the Airport Express' "WDS Relay" mode.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;It might be fun to pick up a &lt;a href="http://www.linksys.com/servlet/Satellite?c=L_Product_C2&amp;amp;childpagename=US%2FLayout&amp;amp;cid=1133202177241&amp;amp;pagename=Linksys%2FCommon%2FVisitorWrapper&amp;amp;lid=7724139789B04"&gt;WRT54GL&lt;/a&gt;. (The 54G has been simplified and will no longer work with most custom firmware.  The 54GL restores the missing features. It appears to have been created specifically so that people can continue to use alternative firmware.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-5002587506959127680?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/5002587506959127680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=5002587506959127680' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5002587506959127680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5002587506959127680'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/08/i-got-new-ethernet-ready-printer-today.html' title='Experiments in Firmware Hacking'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3065280348305785739</id><published>2008-08-03T22:25:00.006-04:00</published><updated>2008-08-04T01:31:58.147-04:00</updated><title type='text'>Date Formatting in Javascript</title><content type='html'>&lt;h2&gt;Problem&lt;/h2&gt;&lt;p&gt;I found myself doing some stuff in Javascript. In particular, I needed to be able to turn a Date into HTML specific to the application that I'm working on. Let's say that the server sends us a task's creation date. We need to format it:&lt;pre class="code"&gt;var fromServer = Date.parse("Sun Aug 03 2008 23:12:52 GMT-0400 (EDT)");&lt;br /&gt;&lt;br /&gt;td.innerHTML = format_date(fromServer);&lt;br /&gt;&lt;br /&gt;&amp;lt;td&amp;gt;&lt;br /&gt;    &amp;lt;span class="date"&amp;gt;2008-08-03&amp;lt;/span&amp;gt;&amp;lt;span class="time"&amp;gt;11:12 PM&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;/pre&gt;However, and I have no guarantee that it will always be a parseable date. In fact, the server sends '-' for dates that don't exist. We still need to output something.&lt;pre class="code"&gt;var fromServer = "-";&lt;br /&gt;&lt;br /&gt;td.innerHTML = format_date(fromServer);&lt;br /&gt;&lt;br /&gt;&amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;I would like format_date to accept either a Date that should be formatted, or a string that should be passed along verbatim (with only necessary HTML character entity escaping). How can we do this in an object-oriented fashion?&lt;/p&gt;&lt;h2&gt;Attempt 1&lt;/h2&gt;&lt;p&gt;The default object-oriented mindset would encourage us to use polymorphism. We have an object, and we want to be able to call a method on that object. Well, we have a Date object.&lt;/p&gt;&lt;p&gt;Since this is Javascript, we could stick an extra method onto Date.prototype that would let us do this. While we're at it, we can put a similar function onto String.prototype:&lt;pre class="code"&gt;Date.prototype.formatAsAppSpecificHTML = function() {&lt;br /&gt;    return "&amp;lt;span&amp;gt;" + this.getFullYear() + ... + "&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;" + this.getHours() ... + "&amp;lt;/span&amp;gt;";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;String.prototype.formatAsAppSpecificHTML = function() {&lt;br /&gt;    return this;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function format_date(o) {&lt;br /&gt;    return o.formatAsAppSpecificHTML();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;There are some problems with this.&lt;ol&gt;&lt;li&gt;We have an ugly, ugly method name. This is because we're mixing abstractions. A Date, in general, shouldn't know how to format itself in this way. Why not? Because it probably doesn't apply to most usages of Date. It may be a common behavior in &lt;em&gt;my&lt;/em&gt; application, but its a nonsensical behavior in &lt;em&gt;your&lt;/em&gt; application. Since the method is very context-specific, the name has to be equally specific.&lt;/li&gt;&lt;li&gt;This only works in an "open" language that lets us add methods to an existing class/prototype (Ruby and Lua (and arguably C#) fall into this camp, Java and C++ do not). Even if your language has the necessary support, you still have to wonder whether it's a good idea to handle stuff this magical.&lt;/li&gt;&lt;li&gt;It's not obvious. You don't normally connect Date and String. You don't expect to see methods shared between them. They are very orthogonal primitives. Yet we've tied them together in an unnatural way. In order for somebody to discover this, they need to think to look in two (potentially distant) places in the code.&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;&lt;h2&gt;Attempt 2&lt;/h2&gt;&lt;p&gt;Polymorphism is a form of runtime decision making. Rather than use language constructs (such as 'if' and '? :'), polymorphism leverages the power of pointers. Since Polymorphism created some problems in this example, what if we switch to use a more traditional (i.e. not object-oriented) solution?&lt;pre class="code"&gt;function format_date(o) {&lt;br /&gt;    if (o.constructor === Date) {&lt;br /&gt;        return "&amp;lt;span&amp;gt;" + o.getFullYear() + ... + "&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;" + o.getHours() ... + "&amp;lt;/span&amp;gt;";&lt;br /&gt;    } else {&lt;br /&gt;        return o.toString();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;This approach also creates some problems. We've simply moved the complexity further up the ladder. Before, the knowledge that a task may or may not have a creation date was strewn across 2 types: Date and String. Looking at either type in isolation, you only see half of the picture. You might not realize that you can take &lt;em&gt;anything&lt;/em&gt; that comes from the server and format it correctly. Now, however, that knowledge is pushed in your face. We're making explicit decisions about concrete types wherever we need to. It's easy to get it right once. So far, we're only handling formatting. What if we also want to draw a timeline? What if we want to find the earliest task in a list of tasks? What if you want to relate a task to source control submissions that occurred while the task was active. In all of these cases, you will need to deal with the fact that a task might or might not have a starting date. At some point, somebody's going to forget that they need to check this, and there's going to be a bug.&lt;/p&gt;&lt;h2&gt;Attempt 3&lt;/h2&gt;&lt;p&gt;If object-oriented didn't work, and procedural didn't work, what are we going to do? Well, actually, I lied. The first attempt used one form of object-oriented abstraction. There are many more. Both of the attempts so far have suffered from primitive obsession. They dealt with both Strings and Dates. In actuality, we don't want to concern ourselves with either of these. We actually have something different - we have an OptionalDate. An OptionalDate knows whether it represents an actual date or whether it represents no date at all. It can format itself correctly in either case, and can be compared to other OptionalDates for sorting purposes. In fact, OptionalDate handles any operation that needs to work with both actual dates and "not dates".&lt;pre class="code"&gt;function format_date(o) {&lt;br /&gt;    return o.format();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OptionalDate(d) {&lt;br /&gt;    this.d = d;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OptionalDate.prototype.format() {&lt;br /&gt;    if (d) {&lt;br /&gt;        return "&amp;lt;span&amp;gt;" + this.d.getFullYear() + ... + "&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;" + this..getHours() ... + "&amp;lt;/span&amp;gt;"&lt;br /&gt;    } else {&lt;br /&gt;        return "-";&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;What makes this a better solution? After all, the code looks very similar to the code in attempt 2.&lt;ol&gt;&lt;li&gt;It localizes the code better than attempt 2. Rather than checking for the presence of a date all throughout your code base, you can collect all those if/else statements in one place. You also get the chance for some pretty cool higher-order programming, where OptionalDate has a method that takes a function to be called if the OptionalDate actually has a date.&lt;/li&gt;&lt;li&gt;It also gives us a better place to hang domain-specific code. Hey, business logic has to live somewhere. It never seems right to put it on the primitive objects, and it also doesn't make sense to put it at the highest level of abstraction. Business logic is the foundation upon which you build an application. As a foundation, it needs a separate place to live.&lt;/li&gt;&lt;li&gt;It makes more sense. When a new programmer is brought onto the team, they will be able to better understand just what is going on. This is extremely important. I believe that, if a person ever has a question about the code, it's a good sign that the code should change. That doesn't mean that you actually take the time to refactor the code, but it's a sign that this is a place that could use some attention.&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;&lt;h2&gt;My Solution&lt;/h2&gt;&lt;p&gt;In the end, I went with something close to Attempt 2. This was actually my first choice; Attempt 1 was purely synthetic. I'm working with a legacy code base, and I'm a little wary about introducing big changes just yet. I'm also very conscious about time. In any case, this is an improvement over what was there before (it just treated the date/time as an opaque string, which wouldn't work at all for my requirements).&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;There are definitely some problems for which the object-oriented noun/verb paradigm breaks down. Or, perhaps stated more precisely, there are problems where that paradigm confuses more than it helps. However, that isn't true in this case. We were able to introduce some good refactorings even while staying true to the spirit of good design.&lt;/p&gt;&lt;p&gt;You may wonder why I care so much. I mean, any of the attempts would have solved the problem perfectly well. Why spend time even thinking about it? I believe that pragmatism is an important trait in programmers, but so too is learning. Whenever you start working on a problem, you need to choose a direction to pursue. Until you start walking, you'll never make progress. You may find yourself at a dead-end, but you wouldn't have known if you hadn't gone that way. My goal is to develop a strong enough design sense that the path that I choose with little thought tends to be one that will work out in the long run. Programming is both tactical and strategic. Most programmers develop their tactical skill as a natural part of writing code. I'm trying to sharpen my strategic skill.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3065280348305785739?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3065280348305785739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3065280348305785739' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3065280348305785739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3065280348305785739'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/08/date-formatting-in-javascript.html' title='Date Formatting in Javascript'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-2708099110133085149</id><published>2008-08-03T21:59:00.003-04:00</published><updated>2008-08-03T22:01:15.267-04:00</updated><title type='text'>Blogger Timestamping</title><content type='html'>&lt;p&gt;Interesting thing about Blogger - it looks like the "Posted by... at..." clause uses the time the post was started, not the time that you eventually push the big Publish button.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-2708099110133085149?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/2708099110133085149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=2708099110133085149' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2708099110133085149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2708099110133085149'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/08/blogger-timestamping.html' title='Blogger Timestamping'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-2334337370488641014</id><published>2008-08-03T20:27:00.003-04:00</published><updated>2008-08-03T21:58:58.489-04:00</updated><title type='text'>F is for Fail</title><content type='html'>&lt;p&gt;It's really sad to see code copied-and-pasted. I feel like a teacher who is grading a test, only to find that two kids have exactly the same answers. It's even sadder to see copied-and-pasted comments. That's like seeing that two kids have exactly the same answers, and both kids' answers are wrong.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-2334337370488641014?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/2334337370488641014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=2334337370488641014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2334337370488641014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2334337370488641014'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/08/f-is-for-fail.html' title='F is for Fail'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-5525480813987919200</id><published>2008-07-21T20:19:00.002-04:00</published><updated>2008-07-21T21:03:04.386-04:00</updated><title type='text'>Object Creation in Ext JS</title><content type='html'>&lt;p&gt;&lt;a href="http://extjs.com/"&gt;Ext JS&lt;/a&gt; has a useful object creation pattern. Most constructors can be passed a hash of configuration parameters. This can be used instead of the &lt;a href="http://en.wikipedia.org/wiki/Prototype_pattern"&gt;prototype creation pattern&lt;/a&gt; (not to be confused with Javascript's &lt;a href="http://en.wikipedia.org/wiki/Prototype-based_programming"&gt;prototypical inheritance&lt;/a&gt;). Rather than create new objects that are copies of an existing object, you create objects based on a set of configuration data.&lt;/p&gt;&lt;p&gt;You want an example? OK. Today, I was creating context menus for items in a tree. There is a global pool of possible actions, and each node responds to a different subset of them. When the user right-clicks on a node, I need to &lt;ol&gt;&lt;li&gt;Create a Menu instance&lt;/li&gt;&lt;li&gt;Add all of the appropriate menu items to it&lt;/li&gt;&lt;li&gt;Show the menu&lt;/li&gt;&lt;/ol&gt;I had hoped that I could create one menu item instance per possible action (20 or so), and then re-use them in different Menu instances. &lt;pre class="code"&gt;var actionMenuMap = {&lt;br /&gt;    addChild: new Ext.menu.Item({text:"Add child", icon:"add.png"}),&lt;br /&gt;    delete: new Ext.menu.Item({text:"Delete", icon:"delete.png"}),&lt;br /&gt;    fireZeMissiles: new Ext.menu.Item({text:"Fire ze Missiles!", icon:"fire.png"})&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var nodeActions = ["addChild", "delete"];   //in practice, this would come from the node itself&lt;br /&gt;&lt;br /&gt;//dangerous nesting ahead!&lt;br /&gt;new Menu({&lt;br /&gt;    items: nodeActions.map(function(a) {&lt;br /&gt;        return actionMenuMap[a];&lt;br /&gt;    })&lt;br /&gt;}).showAt(event.getXY());&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;That didn't work - it seemed like I couldn't share a menu item instance between menu instances. I might be able to get it to work by removing menu items after the menu is dismissed, but I don't actually need to. I can simply hold on to the configuration information.&lt;pre class="code"&gt;var actionMenuMap = {&lt;br /&gt;    addChild: {text:"Add child", icon:"add.png"},&lt;br /&gt;    delete: {text:"Delete", icon:"delete.png"},&lt;br /&gt;    fireZeMissiles: {text:"Fire ze Missiles!", icon:"fire.png"}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var nodeActions = ["addChild", "delete"];   //in practice, this would come from the node itself&lt;br /&gt;&lt;br /&gt;//now we're cooking with functional programming!&lt;br /&gt;new Menu({&lt;br /&gt;    items: nodeActions.map(function(a) {&lt;br /&gt;        return new Ext.menu.Item(actionMenuMap[a]);&lt;br /&gt;    })&lt;br /&gt;}).showAt(event.getXY());&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;In case you are not familiar, map is a function that is present in every functional language and many dynamic languages. It does not exist in Javascript natively, but is added by &lt;a href="http://www.prototypejs.org/api/enumerable/collect"&gt;Prototype&lt;/a&gt;, &lt;a href="http://docs.jquery.com/Utilities/jQuery.map#arraycallback"&gt;jQuery&lt;/a&gt;, &lt;a href="http://api.dojotoolkit.org/jsdoc/dojo/HEAD/dojo.map"&gt;dojo&lt;/a&gt;, &lt;a href="http://www.mochikit.com/doc/html/MochiKit/Base.html#fn-map"&gt;Mochikit&lt;/a&gt;, and probably every other Javascript framework. Here's a sample implementation for reference: &lt;pre class="code"&gt;Array.prototype.map = function(f) {&lt;br /&gt;    if (typeof(f) !== "function") {&lt;br /&gt;        throw new Error("map takes a function");&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    var result = new Array(this.length);&lt;br /&gt;    for (var i = 0; i &lt; this.length; ++i) {&lt;br /&gt;        result[i] = f(this[i]);&lt;br /&gt;    }&lt;br /&gt;    return result;&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;What could possibly make this better? In addition to taking a hash, allow the constructor to take a function. That function manipulates the object after the rest of the construction runs, allowing you to add children or manipulate settings or calculate values. This would be pure icing, of course. Factory functions also feel a lot more lightweight to me than &lt;a href="http://en.wikipedia.org/wiki/Factory_object"&gt;factory objects&lt;/a&gt;. See also &lt;a href="http://api.rubyonrails.org/classes/Object.html#M000018"&gt;Rails' version of the K combinator&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-5525480813987919200?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/5525480813987919200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=5525480813987919200' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5525480813987919200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/5525480813987919200'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/07/object-creation-in-ext-js.html' title='Object Creation in Ext JS'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3722139964397143853</id><published>2008-07-12T00:36:00.002-04:00</published><updated>2008-07-12T01:26:09.231-04:00</updated><title type='text'>Please encode the patterns that you see</title><content type='html'>&lt;p&gt;As my software career has advanced, I've steadily become more cynical and bitter. This isn't good, and it's something that I'm trying to work on. It's hard to go a day without having one of those "you've got to be kidding me" moments. "I can't believe they did it like that." "What were they thinking?" "Hasn't anybody ever tried to do this before?" "There has to be an easier way."&lt;/p&gt;&lt;p&gt;Case in point: I recently read that SWFObject was THE way to embed swf files in your HTML. Fine, I though. I'll give it a try. Within minutes, I ran into something simple that it couldn't do. When you call the function that embeds the swf, it might not actually do the operation immediately - it might wait until the page initialization has progressed beyond a certain point, and then inject the code. I needed to run some code AFTER the swf had been embedded in the page (for sure). There's no way that I could see to do this with the stock SWFobject. Despite the fact that SWFObject inherently depends on event dispatch to work, it doesn't dispatch enough of its own events to be useful to me.&lt;/p&gt;&lt;p&gt;As I started to look at the code to see if I could work it in, I stumbled across the following snippet:&lt;pre class="code"&gt;var att = {};&lt;br /&gt;if (attObj &amp;&amp; typeof attObj === OBJECT) {&lt;br /&gt;    for (var i in attObj) {&lt;br /&gt;        if (attObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries&lt;br /&gt;            att[i] = attObj[i];&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;"Wow!" I thought. "That looks like it would be useful to lots of people." As it turns out, it is very useful - even to the SWFObject developers, who used the same pattern twice more in the same 58-line function:&lt;pre class="code"&gt;var par = {}; &lt;br /&gt;if (parObj &amp;&amp; typeof parObj === OBJECT) {&lt;br /&gt;    for (var j in parObj) {&lt;br /&gt;        if (parObj[j] != Object.prototype[j]) { // Filter out prototype additions from other potential libraries&lt;br /&gt;            par[j] = parObj[j];&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;if (flashvarsObj &amp;&amp; typeof flashvarsObj === OBJECT) {&lt;br /&gt;    for (var k in flashvarsObj) {&lt;br /&gt;        if (flashvarsObj[k] != Object.prototype[k]) { // Filter out prototype additions from other potential libraries&lt;br /&gt;            if (typeof par.flashvars != UNDEF) {&lt;br /&gt;                par.flashvars += "&amp;" + k + "=" + flashvarsObj[k];&lt;br /&gt;            }&lt;br /&gt;            else {&lt;br /&gt;                par.flashvars = k + "=" + flashvarsObj[k];&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;That general pattern appears at least 9 times in the code. It's a good thing they put it in a reusable functio... oh wait, they didn't. Instead, they dutifully pasted the code (comment and all) 8 times.&lt;/p&gt;&lt;p&gt;OK, something's going on. Maybe I've become a much better programmer in the past year; maybe everybody else is getting worse; maybe I'm off my rocker; maybe there's some strange performance or compatibility tweak that prevented them from doing what seems plainly obvious to me. Removing the obvious duplication would have simplified their code, making it more readable (and possibly obviating the need for the accompanying comment).&lt;/p&gt;&lt;p&gt;It's the little things like this that make me wonder if we're actually moving the state-of-the-art forward at all. If we can't see (or don't care about) the duplication at the micro scale, how will we re-use software at any broader level?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3722139964397143853?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3722139964397143853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3722139964397143853' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3722139964397143853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3722139964397143853'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/07/please-encode-patterns-that-you-see.html' title='Please encode the patterns that you see'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3707622935058777768</id><published>2008-06-26T19:50:00.002-04:00</published><updated>2008-06-26T20:12:15.186-04:00</updated><title type='text'>Planescape: Effortless</title><content type='html'>&lt;p&gt;I was amazed to learn that &lt;a href="http://en.wikipedia.org/wiki/Planescape_Torment"&gt;Planescape: Torment&lt;/a&gt;, a game released in 1999, is still playable under Vista. I installed it the other night (intending to finish it this time), patched it (using the most recent official patch, which is probably 8 years old), and started it without a hitch. I'm sure some combination of Microsoft's &lt;a href="http://blogs.msdn.com/oldnewthing/"&gt;fervent dedication to backwards compatibility&lt;/a&gt;, nVidia's seemingly rock-solid drivers, and &lt;a href="http://en.wikipedia.org/wiki/Black_Isle_Studios"&gt;Black Isle's&lt;/a&gt; crack team of developers made this possible.&lt;/p&gt;&lt;p&gt;Recently, Scott Hanselman &lt;a href="http://www.hanselminutes.com/default.aspx?showID=117"&gt;interviewed Steven Frank&lt;/a&gt; of &lt;a href="http://www.panic.com/"&gt;Panic&lt;/a&gt; (a Mac software developer). In the interview, it was revealed that Mac users are less worried about backwards compatibility. Apple has completely axed support for Classic mode, which pretty much kills any software more than 8 years old. As a Mac user, I agree. I'm not that worried about old applications not working. But that's because I'm talking about applications. If an old application stops working, there's a good chance that somebody has written a more recent, and probably better, replacement.&lt;/p&gt;&lt;p&gt;Games are a different beast, though. Games are closer to movies or novels. How would people feel if they couldn't read Les Misérables anymore? Or if somebody went and mucked with Star Wars, and then threatened to not publish the theatrical release anymore? Well, we know what happens then: thousands of fans get really really angry. People crave old stuff. Really great content is timeless. Games should be treated the same way. Ever since "game designer" has become an official title, games have been steadily becoming more than children's amusements. There's often some great narrative buried in there, wrapped up tight inside a husk of decaying code. Eventually, that code won't run on new computers, and the narrative will be lost forever.&lt;/p&gt;&lt;p&gt;Of course, now we have &lt;a href="http://www.vmware.com/products/fusion/"&gt;virtualization&lt;/a&gt;, so maybe it's not such a big problem.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3707622935058777768?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3707622935058777768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3707622935058777768' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3707622935058777768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3707622935058777768'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/06/planescape-effortless.html' title='Planescape: Effortless'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-677563496461059485</id><published>2008-06-26T19:47:00.002-04:00</published><updated>2008-06-26T19:50:39.753-04:00</updated><title type='text'>On Succinctness</title><content type='html'>One of the reasons that I've been blogging has been to help me communicate succinctly. I've been trying to keep my posts small without being lifeless. I want to balance terseness with expressiveness. If you read this blog, how am I doing? What could I do better?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-677563496461059485?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/677563496461059485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=677563496461059485' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/677563496461059485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/677563496461059485'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/06/on-succinctness.html' title='On Succinctness'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8309925574647868078</id><published>2008-06-26T19:07:00.004-04:00</published><updated>2008-06-30T01:42:55.068-04:00</updated><title type='text'>On Usability</title><content type='html'>&lt;p&gt;I've been slowly learning that, in my heart, I'm a usability guy. I'm always concerned for the experience that end users will have with software. I care about making it easier for people to do what they want to do. When it comes to user interfaces, I like bling, but only when it helps the user accomplish a task or understand an interaction. I love &lt;a href="http://en.wikipedia.org/wiki/Exposé_(Mac_OS_X)"&gt;Expose&lt;/a&gt;. It's such a simple but powerful concept. It's also made for a great demo. I hate &lt;a href="http://www.youtube.com/watch?v=yWhmty4r0ds"&gt;Flip 3D&lt;/a&gt;. It's pretty, but also pretty pointless.&lt;/p&gt;&lt;p&gt;I've been interested in API design for a long time, too. As a developer, I have used some libraries that were an absolute joy, and other libraries that were a total mess. My recent foray into Flex (specifically, getting the Tree control to lazily load its data) reminded me what it's like to use a bad API.&lt;/p&gt;&lt;p&gt;I mentioned to a coworker the other day that, if you're developing a library, you should be at last as good a developer as the people that will end up using it. To me, it looked like Adobe (or Macromedia - I don't really know who's responsible) put all of their junior developers on the component library project. As a result, we're blessed with the interface IDataDescriptor, whose methods include &lt;span class="method"&gt;isBranch()&lt;/span&gt; and &lt;span class="method"&gt;hasChildren()&lt;/span&gt;. What's the difference? Should they ever be different? Who knows?!&lt;/p&gt;&lt;p&gt;I realize that I misspoke to my coworker. I should have said that people developing libraries should have a code aesthetic sense. I don't mean code aesthetics in terms of indentation or brace style or what have you. I mean that API designers need to consider the people that will end up using the library. Just as the hallmark of an &lt;a href="http://developer.apple.com/wwdc/ada/index.html"&gt;Apple Design Award&lt;/a&gt; winner is that users "just get it", a good API should also be so easy to understand that it seems obvious to you. As in, "why would anybody make this API any different?"&lt;/p&gt;&lt;p&gt;Design is hard. It takes a conscious effort (well, for most of us). That's why it's important to do. If you're a developer, ask yourself: is this easy enough to understand. Remember, you're writing for an audience - your code does not exist for your eyes only. Even if it did, you're liable to forget everything before you come back to work on it again. If you're a manager, make sure there's enough space for developers to focus on this. If it looks like you're going to miss a deadline, that doesn't give you carte blanche to pressure developers out of finishing their work. Just because the code works doesn't mean it's done.&lt;/p&gt;&lt;p&gt;If you can't make an API grokkable, at least document it well. The standard Java and MSDN docs are full of nonsense.&lt;/p&gt;&lt;blockquote&gt;setFoo(IFurple f) - sets foo to f&lt;/blockquote&gt;&lt;p&gt;Really? I wouldn't have guessed. This tells me nothing. What is foo and why do I care. Why is foo a furple? If anybody ever asks a question about your API, it's a sign that something needs to change. Maybe you just need to add some documentation. Maybe you need a high-level wiki page explaining what the library is trying to do. Maybe you need to rename a method. Maybe you really do need to change the API. Or maybe you just need to smack the developer on the head and tell them to read the Javadocs first. But if you don't change anything, the problem is just going to repeat itself. Who knows - maybe you'll discover a bug or race condition along the way.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8309925574647868078?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8309925574647868078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8309925574647868078' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8309925574647868078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8309925574647868078'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/06/on-usability.html' title='On Usability'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4230981701581616735</id><published>2008-06-23T21:26:00.006-04:00</published><updated>2010-01-18T15:22:02.024-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='vista'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='ui'/><title type='text'>Vista Usabaility... It's Got Issues</title><content type='html'>&lt;p&gt;I bought a Dell with Vista on it a few months ago. I figured that it was high time that I upgraded to Microsoft's newfangled operating system. It hasn't been too bad. There are some hiccups, but every complex piece of software has its share of those. I'm sure Microsoft will try its hardest to fix those with time.&lt;/p&gt;&lt;p&gt;On the other hand, Aero is pretty much terrible. Sure, it's pretty (at least at first). My impression is that the rendering infrastructure is also quite good. But holy cow, Microsoft needs to hire some actual UI designers. Not just artists, but people who understand user interfaces. There are some reasonably amateurish mistakes in Aero. Even if they seemed like good ideas at the time, after using Vista for about a week, you really start to notice them.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;I was first surprised to see how mousovers have invaded Explorer. Look at the following picture. Which icon is selected, and which icon am I pointing my mouse at?&lt;br/&gt;&lt;img src="http://lh5.ggpht.com/bytecrafter/SGBTKmMSKBI/AAAAAAAAACs/j0mO46uAIh4/Highlight.png" /&gt;Can you believe that it's easy to delete the wrong file?&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;The new, unified look for the windows isn't too bad. The window titlebars are translucent, with a frosted glass appearance. At first I though that the effect would be distracting. You know what, I was right.&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/bytecrafter/SGBUK_Am3rI/AAAAAAAAADk/j4V_eUv_Dno/Titlebar.png" /&gt;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Explorer windows look a little bare, but the whole top of the window is available for your dragging pleasure. Well, almost all of it. If you manage to click in the upper-left of the window's title-less-bar, you get your friendly system menu.&lt;br/&gt;&lt;img src="http://lh6.ggpht.com/bytecrafter/SGBUK9xCjRI/AAAAAAAAADc/5wDPXka2PuI/SystemMenu.png" /&gt;&lt;br/&gt;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Finally, my absolute least favorite thing. For whatever reason, the rollovers that cause me to shred the wrong file have also made it into the standard tree control. Here, when your mouse leaves the tree control's bounds, the tree "fades away..." It literally disappears. Which can leave the following unfortunate situation:&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/bytecrafter/SGBTLLVHMHI/AAAAAAAAAC0/ER0VxPy7gXM/Folder01.png" /&gt;&lt;br/&gt;Wait, I think maybe there's a bit more to it...&lt;br/&gt;&lt;img src="http://lh4.ggpht.com/bytecrafter/SGBTLConXZI/AAAAAAAAAC8/vuyUyplCyDQ/Folder02.png" /&gt;&lt;br/&gt;Ah, I bet we can expand that.&lt;br/&gt;&lt;img src="http://lh6.ggpht.com/bytecrafter/SGBTLdw84UI/AAAAAAAAADM/Dwxp8SbB8FA/Folder04.png" /&gt;&lt;br/&gt;There we go!&lt;br/&gt;Now, you might be asking yourself "who cares?" It doesn't seem like it's that big of a problem. True, in Explorer, the only inconvenience is that I don't know at a glance if my folder contains any items. It's a problem, but not a huge one. So, let's take a look at the Java programmer's friend, Eclipse.&lt;br/&gt;&lt;img src="http://lh4.ggpht.com/bytecrafter/SGBWu4ZcJfI/AAAAAAAAADs/ISCKFzv1KJM/EclipseResults.png" /&gt;&lt;br/&gt;Search results, you say. Search results, indeed. And people are &lt;a href="http://weblogs.java.net/blog/kirillcool/archive/2007/02/borrowing_from.html"&gt;trying to emulate it&lt;/a&gt;! Little hint: just because Microsoft does it, doesn't mean it's a good idea.&lt;/p&gt;&lt;p&gt;Even the Windows Classic theme isn't safe. The "expandos" in the classic TreeView fade out too.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;If only Microsoft supported third-party themes... oh wait, you need to replace system DLLs to do that. Still, I don't know if I can stand the horrible usability of the stock UI. Hey Microsoft: ever wonder why so many themes and theming tools exist for your platform? I can think of a reason.&lt;/p&gt;&lt;p&gt;Even with all my bitching, I think Microsoft's heart was in the right place here. They realized that they needed a better rendering infrastructure (with features like compositing on the video card) and a more slick UI. Apple had been stealing the show on this issue for the past &lt;strike&gt;seven&lt;/strike&gt; twenty-three years. Microsoft needs to go back to the drawing board. They need a fresher UI in the next version of Windows. And they need fewer people working on it. I think Windows is going to buckle under its own weight within 10 years. Apple continues to embellish their own UI, but it's often both functional and pretty. That's why people go ga-ga over it. Expose is a great demo, but I use it daily. Same with Dashboard.&lt;/p&gt;&lt;p&gt;I can only imagine what it must have been like to have seen this live:&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/6-fkYFV7rOY&amp;hl=en"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/6-fkYFV7rOY&amp;hl=en" type="application/x-shockwave-flash" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br/&gt;... especially when this is what people were used to...&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Jij5Nzh2Sj4&amp;hl=en_US"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Jij5Nzh2Sj4&amp;hl=en_US" type="application/x-shockwave-flash" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4230981701581616735?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4230981701581616735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4230981701581616735' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4230981701581616735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4230981701581616735'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/06/vista-usabaility-its-got-issues.html' title='Vista Usabaility... It&apos;s Got Issues'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/bytecrafter/SGBTKmMSKBI/AAAAAAAAACs/j0mO46uAIh4/s72-c/Highlight.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6764879559714943384</id><published>2008-06-18T00:07:00.003-04:00</published><updated>2008-06-18T00:24:13.041-04:00</updated><title type='text'>Flex Builder 3 Debugger on Mac OSX</title><content type='html'>&lt;p&gt;I was having a hard time getting Flex Builder on my Mac to behave.  I came across &lt;a href="http://labs.bigspaceship.com/2008/03/19/connecting-flex-to-content-debugger/"&gt;a post at Big Spaceship Labs&lt;/a&gt; that put me on the right path. I tried things, and came up with a reasonable solution, which I added as a comment to the original post.&lt;/p&gt;&lt;p&gt;This technique is useful for keeping several Flash Players on your system (or anything, really). I think I read that &lt;a href="http://michelf.com/projects/multi-safari/"&gt;MultiSafari&lt;/a&gt; uses this trick. The trick is to change an application's bundle identifier. You may have noticed that Mac OSX seems to magically know about all the applications on your system, even though you didn't use an installer for most of them. It uses the bundle identifier (and I believe the bundle version) to keep track of them. I believe that Max OSX treats all applications with the same bundle identifier and version as the same. Internally, they probably form a composite key in a map. Furthermore, most associations (such as "Open With") are made to the bundle identifier, not the application's path. This is so that you can move an application to a new directory without breaking anything. It also means that, if you have 2 applications with the same bundle identifier and version, there is no way to choose which one will launch.&lt;/p&gt;&lt;p&gt;It's surprising how well the whole application list works on Mac OSX. I still had some problems in 10.4, but 10.5 seems to be rock solid in this area. And, when application developers know what they're doing, it works pretty well.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6764879559714943384?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6764879559714943384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6764879559714943384' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6764879559714943384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6764879559714943384'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/06/flex-builder-3-debugger-on-mac-osx.html' title='Flex Builder 3 Debugger on Mac OSX'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6715718587298457256</id><published>2008-06-08T23:11:00.002-04:00</published><updated>2008-06-08T23:33:14.082-04:00</updated><title type='text'>Exceptions in Multithreaded Programming</title><content type='html'>&lt;p&gt;Exceptions are great. I've used them since C++. It's even sweeter that more modern languages (Java, C#, Ruby, etc.) have enhanced them with information like stack traces and nested exceptions. Procedural programming will never go back to error codes.&lt;/p&gt;&lt;p&gt;However, exceptions are increasingly insufficient for the work that I do. I suspect that this is true of everyone. Anybody who has done multithreaded programming in Java knows this problem:&lt;pre class="code"&gt;class WorkItem implements Runnable {&lt;br /&gt;    @Override&lt;br /&gt;    public void run() {&lt;br /&gt;        try {&lt;br /&gt;            //do some work&lt;br /&gt;        } catch (SomeException e) {&lt;br /&gt;            //WHAT DO WE DO HERE?&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Exceptions fall apart in a multithreaded environment. Take the example. What are our choices? We could silently swallow the exception (bad style, and also probably wrong). We could write an error to stderr and quit (bad for users). We could throw a RuntimeException (which will forcefully terminate the thread, which might not be too bad). In practice, we should probably notify somebody. But whom? Exceptions fit so nicely into the framework of the call stack - simply unwind the stack until you find an appropriate exception handler. In a multithreaded environment, you have several independent call stacks. It's likely that the code which will respond to the error is running in a different thread. It's insufficient to unwind one thread's call stack - you need a cross-thread error handling mechanism.&lt;/p&gt;&lt;p&gt;What's the solution in Java? I don't know. Erlang's approach is interesting. In Erlang, you can link processes together. By default, any error in one process will kill every linked process. That sounds bad, but it's a reasonable default. You can instead register yourself as a "system" process. Rather than being outright killed by friendly fire, system processes are informed of their comrades' fate. They are able to decide what to do about it. They could restart the failed process, or send a notification, or log it, or whatever. The important thing is that it's automatic. Once you've linked the processes together, the Erlang runtime takes care of notifying other processes about fatal errors and so forth. It's pretty neat&lt;/p&gt;&lt;p&gt;Maybe I'll eventually get to write something in Erlang. I don't know how easy it is to use in day-to-day life, but it seems pretty awesome in theory.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6715718587298457256?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6715718587298457256/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6715718587298457256' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6715718587298457256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6715718587298457256'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/06/exceptions-in-multithreaded-programming.html' title='Exceptions in Multithreaded Programming'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-405055462417971053</id><published>2008-06-04T18:03:00.003-04:00</published><updated>2008-06-04T19:07:06.498-04:00</updated><title type='text'>Aspect Oriented Programming in Javascript</title><content type='html'>&lt;p&gt;Working with &lt;a href="http://developer.yahoo.com/yui/"&gt;YUI&lt;/a&gt; today, I found that I wanted to augment a built-in method. I am using a custom subclass of &lt;a href="http://developer.yahoo.com/yui/treeview/"&gt;TreeView&lt;/a&gt;. My TreeView registers some mouse callbacks for the generated HTML elements that compose the tree. My TreeView needs to know whenever a Node is refreshed (i.e. whenever a Node regenerates its HTML) so that I can re-register all of my callbacks. The standard YUI &lt;a href="http://developer.yahoo.com/yui/docs/YAHOO.widget.Node.html"&gt;Node&lt;/a&gt; base class doesn't do this.&lt;/p&gt;&lt;p&gt;This sounded like the sort of thing that could be solved using &lt;a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming"&gt;Aspect Oriented Programming&lt;/a&gt;. I wanted to run some of my own code after every invocation of some predefined method. It turns out that AOP in Javascript is pretty trivial. There might be a framework out there, but it wasn't even worth looking. My first implementation simply replaced the existing method with one of my own design, which delegated to the original method. After doing this in a few other places, I clobbered together the following function, whose name sucks, to simplify the whole mess.&lt;/p&gt;&lt;pre class="code"&gt;function augment(obj, fn, options) {&lt;br /&gt;    var original = obj[fn];&lt;br /&gt;    &lt;br /&gt;    //make copies of the fields in options in case it changes&lt;br /&gt;    //between now and when the generated method is called &lt;br /&gt;    //(which may be a long time).&lt;br /&gt;    var before = options.before;&lt;br /&gt;    var replace = options.replace;&lt;br /&gt;    var after = options.after;&lt;br /&gt;    &lt;br /&gt;    obj[fn] = function() {&lt;br /&gt;        if (before) {&lt;br /&gt;            before.apply(this, arguments);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        if (replace) {&lt;br /&gt;            //TODO: we should probably send original along with&lt;br /&gt;            //this call, possibly embedded in arguments&lt;br /&gt;            replace.apply(this, arguments);&lt;br /&gt;        } else {&lt;br /&gt;            original.apply(this, arguments);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        if (after) {&lt;br /&gt;            after.apply(this, arguments);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//samples:&lt;br /&gt;augment(YAHOO.widget.TreeView.prototype, "draw", {&lt;br /&gt;   after:function() {&lt;br /&gt;       this.nodeRefreshed(this.getRoot());&lt;br /&gt;   } &lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;augment(YAHOO.widget.Node.prototype, "refresh", {&lt;br /&gt;    after:function() {&lt;br /&gt;        this.tree.nodeRefreshed(this);&lt;br /&gt;    }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;//also needed (for those playing along at home):&lt;br /&gt;YAHOO.widget.TreeView.prototype.nodeRefreshed = function() { };&lt;/pre&gt;&lt;p&gt;It's actually pretty simple. I grab a reference to the original function. We create a new, closed function. It can refer to the original function and the augmentations, but they are not visible outside the closure. That was the primary driver for me - my previous implementation was storing the original functions in global variables, which polluted the global namespace. And we use Function's handy &lt;span class="method"&gt;apply()&lt;/span&gt;, which allows us to be ignorant of the parameters that the original function takes. In the sample, I also register a dummy implementation of &lt;span class="method"&gt;nodeRefreshed()&lt;/span&gt; so that I don't need to perform a null check in my augmented &lt;span class="method"&gt;refresh()&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;One improvement that I would like to make, but is probably impossible, is to make the environment for &lt;span class="method"&gt;replace()&lt;/span&gt; be identical to the environment for the original function. As it is, any variables that are closed by the original function are unreferenceable by the new function. This is particularly problematic when you only want to make minor changes to a function. You copy the original function's source code into replace, and make the minor changes that you need, but it doesn't work because the variable bindings are different. I think you can do this in Lua. &lt;a href="http://www.ruby-doc.org/core/classes/Binding.html"&gt;I know you can do it in Ruby.&lt;/a&gt; I don't think Javascript has the necessary support yet (and, since we always need to support IE, the necessary support will likely never be available).&lt;/p&gt;&lt;p&gt;So, it's good to know that AOP is so easy in Javascript. I've never really used AOP before, though I work with people who have, and who seem to like it. However, AOP's utility here is only because of YUI's heavy use of inheritance. &lt;a href="http://bytecrafter.blogspot.com/2008/05/why-inheritance-is-bad.html"&gt;I've ranted against that before.&lt;/a&gt; If YUI's TreeControl didn't try to do everything itself, there would be a lot of natural seams that I could use to inject my own logic.&lt;/p&gt;&lt;p&gt;I hope to post soon about my experiences with YUI's TreeControl, &lt;a href="http://developer.yahoo.com/yui/dragdrop/"&gt;Drag and Drop library&lt;/a&gt;, and other components.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-405055462417971053?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/405055462417971053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=405055462417971053' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/405055462417971053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/405055462417971053'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/06/aspect-oriented-programming-in.html' title='Aspect Oriented Programming in Javascript'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3787157459440083432</id><published>2008-05-25T10:49:00.005-04:00</published><updated>2008-05-25T11:37:10.306-04:00</updated><title type='text'>Swing Does Components Better Than Component Libraries Do</title><content type='html'>&lt;p&gt;I'm currently trying to get a handle on &lt;a href="http://www.adobe.com/products/flex/"&gt;&lt;strike&gt;Macromedia&lt;/strike&gt; Adobe Flex&lt;/a&gt; using the free, open-source &lt;a href="http://opensource.adobe.com/flex/"&gt;Flex SDK&lt;/a&gt;. Flex is a set of tools to create flash-based "applications" using XML UI definition and Actionscript for everything else. &lt;/p&gt;&lt;p&gt;It seems that Adobe has adopted some Microsoft conventions for programmatic UI creation and management. In particular, components in Flex have &lt;a href="http://livedocs.adobe.com/flex/3/html/help.html?content=layoutperformance_03.html"&gt;a long and convoluted lifecycle&lt;/a&gt;. In Web Forms, I never understood why I shouldn't create child controls in my Control's constructor; Flex's UIComponent has a &lt;a href="http://livedocs.adobe.com/flex/3/langref/mx/core/UIComponent.html#createChildren()"&gt;&lt;code class="method"&gt;createChildren()&lt;/code&gt;&lt;/a&gt; method. I still don't know why I shouldn't create child controls in the constructor.&lt;/p&gt;&lt;p&gt;In my Swing programming, on the other hand, I don't seem to run across these problems. As far as I know, a Swing component has one and only one step in its initialization process - the constructor. That certainly makes things simpler. I don't have to wonder "What happens if I put this here?" or, more commonly, "Why does this thing not work? Oh, somebody messed up the initialization code."&lt;/p&gt;&lt;p&gt;Actually, most of my frustration here is directed to ASP.NET 1.1.  Things may have gotten better in later versions (though I doubt it), and I don't really know how good or bad it is in Flex land. Still, this dredged up bad memories for me. Thick base classes suck!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3787157459440083432?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3787157459440083432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3787157459440083432' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3787157459440083432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3787157459440083432'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/05/swing-does-components-better-than.html' title='Swing Does Components Better Than Component Libraries Do'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-6364352929959681103</id><published>2008-05-14T22:05:00.004-04:00</published><updated>2008-05-25T11:36:14.340-04:00</updated><title type='text'>Why Inheritance Is Bad</title><content type='html'>&lt;p&gt;3D graphics programmers, if there are any that read this blog, are familiar with &lt;a href="http://en.wikipedia.org/wiki/Gimbal_lock"&gt;gimbal lock&lt;/a&gt;.  The Wikipedia article makes an analogy to compass directions.  If you tell somebody at the north pole to face south, which direction are they facing?  You have no way of knowing, because south loses its meaning at the north pole.  At any other place on the planet, your location and direction are completely independent.  At the poles, direction has no meaning in the cartographic coordinate system.  Two previously independent variables have now become linked.&lt;/p&gt;&lt;p&gt;In programming, when you derive from a base class, the classes are in a state of gimbal lock.  We can vary the subclass as much as we want without affecting the base class.  However, a change to the base class will not only affect our subclass, but every other subclass out there.  Suppose that our base class provides some core data processing functionality, and its various subclasses encapsulate different data sources.  So far, so good.  We've managed to avoid duplication.  If we come up with a new data source, we can simply create a new subclass.  However, what happens if we want an existing data source to send data to a different set of processing rules?  A class would need to have more than one base class, except in a way different from normal multiple inheritance.  No programming language that I've seen can support this scenario.&lt;/p&gt;&lt;p&gt;A hallmark of good object-oriented design is that classes have one and only one responsibility.  Why is this important?  It makes the code simple, it makes the code testable, it decreases coupling, it might increase cohesion, and it's just plain nicer to read.  When you derive from a base class, your class has all of its explicit responsibilities, and the responsibilities of each of its ancestor classes.  In our example, each subclass is itself responsible for knowing the ins and outs of a particular data source.  Since they all derive from a common base class, they are also responsible for knowing how to process the data.  This example only deals with 2 responsibilities, but real systems deal with hundreds.&lt;/p&gt;&lt;p&gt;Remember that inheritance is often used to represent taxonomies.  You might have classes such as Animal &amp;gt; Mammal &amp;gt; Giraffe or Widget &amp;gt; Button &amp;gt; Pushbutton.  You might say that things on the right are defined in terms of things on the left, but that's not exactly true.  A giraffe doesn't have lungs because it's a mammal; we call it a mammal because it has lungs.  Using inheritance to &lt;em&gt;describe&lt;/em&gt; something (using, for example, interface inheritance) makes a lot of sense.  Using inheritance to &lt;em&gt;define&lt;/em&gt; something doesn't.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-6364352929959681103?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/6364352929959681103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=6364352929959681103' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6364352929959681103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/6364352929959681103'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/05/why-inheritance-is-bad.html' title='Why Inheritance Is Bad'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8636788275639178947</id><published>2008-05-14T21:17:00.004-04:00</published><updated>2008-05-14T22:05:45.593-04:00</updated><title type='text'>Adventures with the Scriptaculous Ajax.Autocompleter</title><content type='html'>&lt;p&gt;I had a chance to use the &lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; autocompleter control at work the other day.  Since we are using Ruby on Rails, it was easy enough to use &lt;span class="method"&gt;text_field_with_auto_complete()&lt;/span&gt; to generate the relevant HTML.&lt;/p&gt;&lt;p&gt;The client noted that it was difficult to enter a new word if it was too close to an existing word.  For example, he would type "expensive", and the system would suggest "not expensive".  When he would hit Enter, it would replace what he had typed with "not expensive".  This is because the Scriptaculous control doesn't support the case where no item is selected - one item is always selected, and it defaults to being the first item.&lt;/p&gt;&lt;p&gt;I decided to have a peek at the source code.  In maybe 15 minutes, I had managed to convince it that "nothing selected" is a valid case.  Unfortunately, I didn't refactor the code in any way - now every autocompleter behaves this way.&lt;/p&gt;&lt;p&gt;Scriptaculous uses prototype's &lt;a href="http://www.prototypejs.org/api/class#method-create" class="method"&gt;Class.create()&lt;/a&gt; to generate 2 classes: Autocompleter.Base and its sole subclass: Ajax.Autocompleter.  From the source code:&lt;blockquote&gt;Autocompleter.Base handles all the autocompletion functionality  that's independent of the data source for autocompletion. This includes drawing the autocompletion menu, observing keyboard and mouse events, and similar.&lt;/blockquote&gt;&lt;/p&gt;&lt;p&gt;I appreciate their desire to re-use code. Unfortunately, the use of inheritance has caused the whole mess to suffer some sort of object-oriented &lt;a href="http://en.wikipedia.org/wiki/Gimbal_lock"&gt;gimbal lock&lt;/a&gt;.  It's easy to create a new Autocompleter that gets its data from a different source; it's much more work to create an Ajax autocompleter with different behavior.&lt;/p&gt;&lt;p&gt;If I have some time (that is, when I'm not busy complaining about something), I might see if I can refactor the scriptaculous source to further separate the data source of an autocompleter from its interaction controller, using some method other than inheritance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8636788275639178947?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8636788275639178947/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8636788275639178947' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8636788275639178947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8636788275639178947'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/05/adventures-with-scriptaculous.html' title='Adventures with the Scriptaculous Ajax.Autocompleter'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1908724145235743004</id><published>2008-05-06T18:26:00.005-04:00</published><updated>2008-05-08T00:15:53.187-04:00</updated><title type='text'>Ruby in LISP clothing</title><content type='html'>As it turns out, Ruby's syntax is somewhat flexible.  As an example, parentheses are sometimes optional when calling a method. &lt;div class="code"&gt;puts "Oh hai!"&lt;br /&gt;puts("Oh hai!")&lt;br /&gt;&lt;br /&gt;link_to_remote "Go there", :url =&gt; { :controller =&gt; :there, :action =&gt; :go }&lt;br /&gt;link_to_remote("Go there", { :url =&gt; { :controller =&gt; :there, :action =&gt; :go } })&lt;/div&gt;The only time the parentheses are needed is when the invocation would otherwise be ambiguous:&lt;div class="code"&gt;link_to_remote h text, :url =&gt; { ... }&lt;/div&gt;(h is the HTML escaping function.  link_to_remote may actually automatically escape the HTML, but I'm not sure.)&lt;br /&gt;&lt;br /&gt;Ruby can't tell what the user is trying to do.  The normal way to fix this is to add some parentheses to the call:&lt;div class="code"&gt;link_to_remote(h(text), :url =&gt; { ... })&lt;br /&gt;link_to_remote(h text, :url =&gt; { ... }) &lt;/div&gt;however, it is also possible to use LISP-like parentheses with the same result:&lt;div class="code"&gt;link_to_remote (h text), :url =&gt; {...}&lt;br /&gt;(link_to_remote (h text), :url =&gt; {...})&lt;/div&gt;Which is better?  Certainly one of the first pair is more conventional.  For some reason, though, I found myself migrating to the second pair.  I suspect it's a combination of&lt;ul&gt;&lt;li&gt;I try to avoid using parentheses in method calls in Ruby as much as possible&lt;/li&gt;&lt;li&gt;LISP is awesome&lt;/li&gt;&lt;li&gt;I have done some Objective-C programming, in which you [object send:message]&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/32241238-1908724145235743004?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1908724145235743004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1908724145235743004' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1908724145235743004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1908724145235743004'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/05/as-it-turns-out-rubys-syntax-is.html' title='Ruby in LISP clothing'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-2637558762628652187</id><published>2008-05-01T22:18:00.002-04:00</published><updated>2008-05-01T22:48:55.186-04:00</updated><title type='text'>Consistency on Rails</title><content type='html'>The Ruby on Rails crowd makes a big deal about consistency.  They claim that Rails is easy and fast for development partially because it favors convention over configuration.  I guess the thought is that you don't need to write any configuration.  Plus, once you've worked on a few Rails apps, you pretty much know where everything is.&lt;br /&gt;&lt;br /&gt;So far, I've found a lot of that to be pretty much true.  Migrations, routes, controllers, views, database configuration - there's a place for all of them, and they're pretty much exactly where I would expect them.  However, I've often found myself struggling when actually writing the code.  I'm either going to the documentation constantly, or even peeking in the Rails source.  (It turns out that some options to methods aren't documented, and the rails plugins that we are using have poorer documentation than the core).&lt;br /&gt;&lt;br /&gt;I only have 2 small examples at hand, but I could write about many more annoyances.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="method"&gt;link_to&lt;/span&gt; and &lt;span class="method"&gt;link_to_remote&lt;/span&gt; both seem similar on the surface.  One will generate a normal hyperlink that will navigate the browser; the other will generate an AJAX link that will interpret the resultant response as either HTML to be embedded or Javascript to be executed.&lt;br /&gt;&lt;br /&gt;Both allow you to specify the url using &lt;span class="symbol"&gt;:controller&lt;/span&gt; and &lt;span class="symbol"&gt;:action&lt;/span&gt;.  However, they have very different signatures, and it trips me up everytime.&lt;div class="code"&gt;link_to "Help", { :controller =&gt; :help, :action =&gt; :sample }&lt;br /&gt;link_to_remote "Help", :url =&gt; { :action =&gt; :help, :action =&gt; :sample }&lt;/div&gt;Did you see that?  The only difference between the two is that one requires the URL-related options to be packaged in a hash named &lt;span class="symbol"&gt;:url&lt;/span&gt;, while the other disallows the hash&lt;/li&gt;&lt;li&gt;When using the form helper methods such as &lt;span class="method"&gt;text_field&lt;/span&gt; and &lt;span class="method"&gt;text_field_tag&lt;/span&gt;, you must be careful.  Here is an example:&lt;div class="code"&gt;text_field :post, :name      &amp;lt;input id="foo_bar" name="foo[bar]" size="30" type="text" /&amp;gt;&lt;br /&gt;text_field_tag 'post[name]'  &amp;lt;input id="foo[bar]" name="foo[bar]" type="text" /&amp;gt;&lt;/div&gt;As you can see, if you change from one to the other, you might need to also update you RJS files (or remember to set an explicit ID).  This would have all been avoided if &lt;span class="method"&gt;text_field&lt;/span&gt; had simply generated id and name attributes with the same value.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Rails certainly has its good points, but the small details are sometimes infuriating.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-2637558762628652187?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/2637558762628652187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=2637558762628652187' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2637558762628652187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2637558762628652187'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/05/consistency-on-rails.html' title='Consistency on Rails'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-8284252540524501246</id><published>2008-04-10T21:30:00.010-04:00</published><updated>2008-06-23T18:55:00.130-04:00</updated><title type='text'>Dream Programming Language: Optional Interface Methods</title><content type='html'>Yet again, I find myself disliking abstract classes.  I was creating a subclass of Java's OutputStream called PathalogicallyBufferedOutputStream (for testing purposes).  I wanted to implement flush, which Eclipse nicely autocompleted to look like this:&lt;div class="code"&gt;@Override&lt;br /&gt;public void flush() throws IOException {&lt;br /&gt;    // TODO Auto-generated method stub&lt;br /&gt;    super.flush();&lt;br /&gt;}&lt;/div&gt;That gave me enough pause to wonder... do I actually need to call the super method.  It seems that I have 2 choices:&lt;ol&gt;&lt;li&gt;Check the documentation&lt;/li&gt;&lt;li&gt;Read the source code&lt;/li&gt;&lt;/ol&gt;Fortunately, Eclipse makes it easy to browse the JDK source code.  It only took 2 seconds to verify that the base implementation, in fact, does nothing.  Without Eclipse, I probably would have ignored it.&lt;br /&gt;&lt;br /&gt;The no-op method stub is in the base class so that consumers of any OutputStream can indicate when the bytes that they have written so far form a semantically meaningful chunk.  Somebody might be waiting for some data; this allows you to get it to her.  The problem that I have as a deriver of OutputStream is that, without source code or good documentation, I have no idea whether I should or should not call the base method.  This is not a problem with JDK-provided classes, but it could be a real bear with a third party library.  &lt;br /&gt;&lt;br /&gt;Some people might claim that you should "always call the base method".  If course, that raises the question of at what point one should call the base method.  Should you call it before or after your code?  Perhaps you call it in the middle.  I prefer to assume that I never have to call the base method, because I shouldn't be deriving from concrete classes, and I like to assume that method that I am overriding is simply a stub that I am free to customize.&lt;br /&gt;&lt;br /&gt;If I were designing a language, I would want some notion of "optional interface methods."  I want methods that I can declare on an interface (or perhaps an abstract base class) which implicitly have a no-op implementation.  If I choose to implement one in a subclass or interface implementor (because I want to be notified when something happens), I would get the benefit of code completion.  The caller is not burdened with the need to check to see if I've implemented the method, and the implementor is not burdened with the uncertainty of whether or not to call the base method.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-8284252540524501246?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/8284252540524501246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=8284252540524501246' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8284252540524501246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/8284252540524501246'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/04/dream-programming-language-optional.html' title='Dream Programming Language: Optional Interface Methods'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-887177537239676359</id><published>2008-04-10T18:51:00.003-04:00</published><updated>2008-04-10T22:28:11.940-04:00</updated><title type='text'>Why Message Passing?</title><content type='html'>Back at the end of March, I was starting some work on Ruby on Rails.  I thought that a good April Fool's Day joke would be to release "Lua on a Luge" or "Lua on Skiis" (somebody came up with &lt;a href="HTTP://WWW.COBOLONCOGS.ORG/INDEX.HTM"&gt;something even better&lt;/a&gt;).  As the people that I work with know, I'm a big fan of Lua.  I appreciate the things that it does well (prototype-based programming, and having very simple, consistent syntax).  Even more interesting, though, are the things that it doesn't try to do:&lt;br /&gt;&lt;blockquote&gt;Lua does not try to help you write programs with hundreds of thousands of lines. Instead, Lua tries to help you solve your problem with only hundreds of lines, or even less. - &lt;a href="http://www.lua.org/pil/p1.html"&gt;Programming in Lua - Preface&lt;/a&gt;&lt;/blockquote&gt;In any case, Lua doesn't directly support multithreaded programming.  Normally, this would be fine.  However, I've worked on some .NET and Java web apps that:&lt;ol&gt;&lt;li&gt;Spawn a background thread to do some work&lt;/li&gt;&lt;li&gt;Handle multiple requests at once&lt;/li&gt;&lt;li&gt;Share some object / data between all of the request-handling threads&lt;/li&gt;&lt;/ol&gt;What kinds of things might we share?  We might share configuration information, we might share connection pools (of connections, or threads, or whatever), we might share session information.  Anything that is shared must be concurrency-safe.&lt;br /&gt;Java and .NET provide synchronization mechanisms.  Lua does not.&lt;br /&gt;&lt;br /&gt;I started to think about various ways to solve this problem.  One could expose a C threading library to Lua.  You could expose both the thread management and synchronization primitives.  Then, you could only share objects that are specifically written to use that threading library.  You might also be able to inject metatables into arbitrary objects, but that would only go so far.&lt;br /&gt;&lt;br /&gt;Then I realized that I already knew a better approach.  If we relied on simple message passing between Lua processes, we could avoid the need to synchronize every shared objects - no objects would actually be shared (we might copy all values that get sent along on a message, or restrict the kinds of values that may be sent on a message).&lt;br /&gt;&lt;br /&gt;It would probably not be as efficient as Erlang's message passing.  Also, I don't know of any Lua implementations other than the reference implementation and World of Warcraft's implementation, but I doubt that either support lightweight processes (instead, both implementations provide coroutines).  Incidentally, Ruby on Rails is currently not thread-safe (other Ruby web frameworks apparently are, according to the &lt;a href="http://mongrel.rubyforge.org/wiki/FAQ"&gt;Mongrel FAQ&lt;/a&gt;). At least one Rails guy &lt;a href="http://jxh.bingodisk.com/bingo/public/presentations/JHoffmanRailsConf-Berlin-Sept2007.pdf"&gt;thinks that it should remain that way (see slide 115)&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-887177537239676359?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/887177537239676359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=887177537239676359' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/887177537239676359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/887177537239676359'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/04/why-message-passing.html' title='Why Message Passing?'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-1523983766536113829</id><published>2008-04-10T18:50:00.004-04:00</published><updated>2008-04-10T20:14:26.770-04:00</updated><title type='text'>Dream Programming Language: No Static Fields</title><content type='html'>If I were to implement my own OO programming language, static data would be the first thing to go.  I can't find any compelling reason to keep it around.  &lt;br /&gt;&lt;br /&gt;Something that people may or may not undestand is that &lt;span class="keyword"&gt;static&lt;/span&gt; really means "global".  Seriously, I wish that languages like Java and C# simply  used &lt;span class="keyword"&gt;global&lt;/span&gt; as the keyword instead.  Static methods are simply global methods that have a long name with a period in the middle (well, I suppose they are also scoped and are subject to access control).  Static, final references to immutable objects are just that - they're global constants.  Keeping a single definition of PI around is generally a good thing.  And, you guessed it, static fields are simply global variables in disguise.&lt;br /&gt;&lt;br /&gt;"But!" I hear you say.  "But I need static fields to implement singletons!"  Why do you want singletons?  Sure, you might think that you will only ever want a single instance of a class.  I think you're wrong.  What happens when you want to do some unit testing?  What happens when the requirements change.&lt;br /&gt;&lt;br /&gt;At one point, I'm sure that Windows assumed that it had exactly one display.  If singletons had been widely used then, I would not have been surprised if there were some singleton which kept the display's information (in actuality, it may have simply been a global variable).  Today, we might not only have several physical displays, but we might also have multiple active windowing sessions on a single Windows server.  How about mice?  Surely, a system has no more than one pointer.  Well, that's kinda true, but my coworkers and I wish that we could get a second pointer on the screen for our pairing sessions.  What about my Wacom tablet?  And my friend prototyped a game that was controlled by using two mice.  Singletons are the antithesis of what the &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt; folks have been telling us. &lt;br /&gt;&lt;br /&gt;If I were designing a language, I would want the programming language to encourage good programming practice.  Since I'm the one designing this language, I get to choose what is good and bad.  And I say, "down with singletons!"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-1523983766536113829?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/1523983766536113829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=1523983766536113829' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1523983766536113829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/1523983766536113829'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/04/dream-programming-language-no-static.html' title='Dream Programming Language: No Static Fields'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-604319124947298942</id><published>2008-04-06T22:17:00.006-04:00</published><updated>2008-04-06T23:20:06.280-04:00</updated><title type='text'>Message Passing</title><content type='html'>Anybody who has tried to do multithreaded programming in Java, C#, or most any C-based language finds that it can quickly become nightmarish.  Deadlocks, race conditions, lock management - all of these issues will occur.  In Java, all threads are able to share objects.  As a result, the programmer is responsible for ensuring that his objects can't be put into an invalid state.  Java provides &lt;a href="http://java.sun.com/docs/books/tutorial/essential/concurrency/syncmeth.html"&gt;per-object monitors&lt;/a&gt; to enable this.  However, working with such monitors can be a nightmare.  Using &lt;span class="method"&gt;notifyall&lt;/span&gt; when you should have used &lt;span class="method"&gt;notify&lt;/span&gt;, or forgetting to add the &lt;span class="keyword"&gt;synchronized&lt;/span&gt; keyword to a method declaration, or incorrectly assuming that a class was thread-safe when it wasn't; these are all examples of simple problems that can cause your application to fail (perhaps only sometimes).&lt;br /&gt;&lt;br /&gt;Erlang, on the other hand, does not suffer the same problems.  Instead of sharing things, Erlang processes (which are the Erlang unit of concurrency) do not share anything.  As a result, the need for synchronization goes away.  Erlang processes send messages to each other.  Each process has a mailbox.  Each process can retrieve and process messages from its mailbox.  You can imagine that a message is copied when it is put into another process' mailbox, but in actuality, since data in Erlang is immutable, the system is probably able optimize that away.&lt;br /&gt;&lt;br /&gt;One of Java's founding principles was that manual memory management is complex, and thus easy to get wrong.  Far simpler was to provide a system where the programmer didn't need to worry about such things.  Erlang does for concurrency what garbage collection did for memory management - it removes that responsibility from the developer.  Instead of asking every developer to handle any threading issues that might arise, it provides an abstraction that hides all of them.  Behind the scenes, Erlang's mailbox system does whatever synchronization it needs to do in order to be completely thread safe. &lt;br /&gt;&lt;br /&gt;Of course, Erlang's approach isn't perfect.  Just as Java's garbage collection can sometimes allow memory to leak (when you are holding onto a reference longer than you need to), Erlang's system could still be susceptible to problems such as deadlocks.  However, it is less likely to occur, and should be more obviously solvable when it happens.&lt;br /&gt;&lt;br /&gt;Erlang's approach is so radical, yet so simple, that it's definitely worth learning more about.  I hope to see Java and C# projects adopt Erlang's philosophy when appropriate.  In performance-critical applications, manual concurrency management might be the way to go.  For most purposes, though, the safety provided by Erlang's approach outweighs any negative side effects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-604319124947298942?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/604319124947298942/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=604319124947298942' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/604319124947298942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/604319124947298942'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/04/tools-for-concurrency.html' title='Message Passing'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-7652893835845763017</id><published>2008-04-02T00:25:00.002-04:00</published><updated>2008-04-02T00:39:53.563-04:00</updated><title type='text'>GameStop culture</title><content type='html'>Unlike &lt;a href="http://www.penny-arcade.com/comic/2007/10/31/"&gt;some people&lt;/a&gt;, I actually have a decent GameStop nearby.  The people who work there are intelligent, useful, and just generally pleasent.  I was there tonight, picking up a copy of &lt;a href="http://en.wikipedia.org/wiki/Condemned:_Criminal_Origins"&gt;Condemned&lt;/a&gt;, which I have heard good things about.&lt;br /&gt;&lt;br /&gt;In any case, the clerk was telling me that its sequel is also good, and noted that it is more of a horror game (Condemned being more of a thriller).  As I was leaving, he suggested that, if I beat it in less than a week, I could trade it in towards its big brother.  I thought he said something about full credit.  &lt;br /&gt;&lt;br /&gt;This struck me as odd.  Here was a Gamestop employee essentially offering to loan me a game for a week, with no risk to me.  Well, I suppose I would be turning $17 of hard currency in GameStop store credit, which is good for them, but it still seems odd.  Then again, their whole &lt;a href="http://www.penny-arcade.com/comic/2007/03/30/"&gt;business model&lt;/a&gt; is odd.  &lt;br /&gt;&lt;br /&gt;On the other hand, perhaps I simply misheard him.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-7652893835845763017?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/7652893835845763017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=7652893835845763017' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7652893835845763017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7652893835845763017'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/04/gamestop-culture.html' title='GameStop culture'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4050445296363267991</id><published>2008-03-31T18:49:00.006-04:00</published><updated>2008-03-31T20:04:03.761-04:00</updated><title type='text'>Ruby on Rails after 3 days</title><content type='html'>I've been learning Ruby on Rails at work for the past 3 days.  I had never really worked with Rails before, though I had certainly read enough to form misguided opinions about what was good and what was bad. After some hands-on experience, I have a better idea of what I do and don't like.&lt;br /&gt;&lt;h2&gt;Routes&lt;/h2&gt;Routes are Rails' way to find the handler for a given request.  You can establish routes by editing routes.rb.  I don't know whether this is common practice, but an advantage of using a Ruby source file over XML is that you can use Ruby language and library features.  This makes it easier to add conditional or generated routes, something that the Rails REST support does with abandon.&lt;br /&gt;&lt;br /&gt;Routes are also 2-way.  They are also used when you want to know the URL to a particular controller/action.  This makes virtually impossible for your URL recognition and URL creation code to get out of sync.&lt;br /&gt;&lt;h2&gt;RJS&lt;/h2&gt;RJS is Ruby's javascript-generation library.  It's used as part of infrastructure for AJAX applications.  Most of the Ruby AJAX &lt;a href="http://www.rubynoob.com/articles/2006/05/13/simple-rails-rjs-tutorial"&gt;tutorials&lt;/a&gt; that I've seen do something that I consider to be very bad practice - they run code on the server that doesn't need to be.  The typical pattern seems to be to use an RJS helper method to emit Javascript that attaches a listener to some other HTML element.  This listener then issues an XmlHttpRequest to the server, which produces Javascript code.  This code is then sent to the client, where it is eval()'d.  While there is nothing wrong with that pattern per se, imagine doing that for something as simple as clearing a text box.  Imagine thousands of clients contacting the server just so that they can clear their text boxes.&lt;br /&gt;&lt;br /&gt;Of course, in typical Rails fashion, there are many ways to use RJS.  Rather than use it in response to a lightweight request to the server, you can also use it when you are building up the original HTML page.  This can both simplify your application and also increase its perceived performance.  Of course, you need to know that you are able to do this.&lt;br /&gt;&lt;br /&gt;Unfortunately, the Javascript that is generated isn't that much different from the original Ruby.  Compare this RJS Ruby code:&lt;br /&gt;&lt;pre&gt;page['query_results'].value = ''&lt;/pre&gt;to this hand-crafted Javascript:&lt;br /&gt;&lt;pre&gt;window['query_results'].value = '';&lt;/pre&gt;I'm generally against code-which-generates-code, and especially when used to avoid learning a new programming language.  Web developers usually end up developing a reasonable understanding of HTML, and those in the Ruby community seem to also be reasonably proficient with CSS.  I would hate for them to miss out on a fantastic language like Javascript.&lt;br /&gt;&lt;h2&gt;Ruby Itself&lt;/h2&gt;Finally, Ruby as a language creates some interesting situations.  I find its dynamism to be both a blessing and a curse.  It's pretty amazing that &lt;a href="http://www.aptana.com/"&gt;Aptana Studio&lt;/a&gt; seems to be able to "go to declaration" reasonably well.  Of course, this only works for methods which are defined statically, in the source code.  Some methods are added dynamically (via method_missing or some other black magic of which I am unaware).  I believe that it is impossible to even find out what these are - you simply have to know what methods ActiveRecord may have added to your model objects.  I'm sure that this gets easier with exposure.&lt;br /&gt;&lt;br /&gt;Incidentally, this very dynamism has spawned some very interesting workarounds.  Since the routing tables are stored as data, it's hard if not impossible to see the set of routes by looking at the code.  However, there is a rake target to see the contents of the routing table: &lt;pre&gt;rake routes&lt;/pre&gt;Similarly, the migration nature of Rails database management makes it quite hard to quickly grok the structure of the database.  However, Rails generates a schema.rb file which theoretically mirrors the current state of your database schema.  Perhaps not strictly necessary, but awfully convenient.&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;In all, I'm reasonably happy with Rails so far.  It's quite different from previous experiences that I've had developing web applications.  In a way, it hearkens back to when I was first learning HTML.  It was trivially easy to write some HTML and then see what it looked like in a web browser.  The code/test cycle was very small.  Rails brings the same sort of simplicity to web application development.  Rather than spend time writing code, you spend time learning how to write the code in the correct Rails mindset.&lt;br /&gt;&lt;br /&gt;I don't know if Rails will scale well.  I don't know if Ruby will be able to keep up.    I don't know if ActiveRecord will strangle us or liberate us.  However, I do know myself, and I know that I will stay skeptical for quite some time.  Hopefully, one day I will realize that I've been happily writing Ruby code for years and find that neither it nor Rails have failed me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4050445296363267991?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4050445296363267991/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4050445296363267991' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4050445296363267991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4050445296363267991'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/03/ruby-on-rails-after-3-days.html' title='Ruby on Rails after 3 days'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-309286206740712015</id><published>2008-03-11T22:38:00.004-04:00</published><updated>2008-04-12T23:31:47.059-04:00</updated><title type='text'>Windows Home Server</title><content type='html'>I just received my &lt;a href="http://h40059.www4.hp.com/mss/"&gt;HP MediaSmart Server&lt;/a&gt; (actually, the &lt;a href="http://www.shopping.hp.com/product/desktop/desktop_hp/storage/4/accessories/GG795AA%2523ABA"&gt;EX470&lt;/a&gt;). I've just finished setting it up and playing with it a bit. &lt;br /&gt;&lt;br /&gt;Here are some problems that I encountered:&lt;br /&gt;  &lt;ol&gt;&lt;li&gt;To get my VMWare Windows XP instance on my Macbook Pro to see the WHS, I needed to put the network connection into Bridged mode.&lt;/li&gt;&lt;li&gt;It didn't initially work with my Airport Extreme base station, but &lt;a href="http://www.homeserverhacks.com/2007/12/configuring-apple-airport-extreme-for.html"&gt;there is a solution&lt;/a&gt; (and not a bad solution at that).&lt;br /&gt;  &lt;/li&gt;&lt;/ol&gt;  Here are some of my findings:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It appeared to be cheaper to buy an extra 500GB drive (even the exact same drive as is in the server) than to buy the EX475.  The drive that is in the first slot is a &lt;a href="http://www.seagate.com/ww/v/index.jsp?vgnextoid=b9df99f4fa74c010VgnVCM100000dd04090aRCRD"&gt;500GB Seagate ST3500630AS&lt;/a&gt; (&lt;a href="http://www.newegg.com/Product/ProductList.aspx?Submit=ENE&amp;DEPA=0&amp;Description=st3500630as&amp;x=22&amp;y=34"&gt;Newegg search&lt;/a&gt;).        &lt;/li&gt;&lt;li&gt;It runs IIS 6.0, ASP.net 2.0.50727, PostgreSQL 8.2.1.7007, &lt;a href="http://www.fireflymediaserver.org/"&gt;Firefly media server (build 1.1 svn-1601)&lt;/a&gt;, .NET 2.0 SP1, and Java 1.4.2_13&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Though it is preferable to administer the server only via the Home Server Console and the shared folders (for adding Add-Ins), it is also possible to remote desktop to the server and get a full Windows shell, start menu and all. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;The HP Photo WebShare web application appears to be written in Java, and is being served by Jetty under IIS using isapi_redirect.dll.  If I knew anything about Jetty or about using a Java application server under IIS, I would tell you more.  Otherwise, the app looks pretty much crap.  It works, but looks very amateurish.  Much like most OEM-supplied software that comes pre-installed on your computer.  On the bright side, it seems to have an ActiveX control to bulk upload images, which is a very nice (and essential) touch.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;If you don't get giddy about the idea of an extensible, autonomous, unobtrusive computing henchman in your domicile, then I suppose Windows Home Server isn't for you. But I personally relish the idea of making my home network a little more than a web browsing gateway. I have great faith in the ability for computers to improve the way we live, and I see this as a very small step towards a seemingly distant, but bright, future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-309286206740712015?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/309286206740712015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=309286206740712015' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/309286206740712015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/309286206740712015'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/03/windows-home-server.html' title='Windows Home Server'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3957476534860850394</id><published>2008-03-10T19:47:00.005-04:00</published><updated>2008-03-10T20:41:23.467-04:00</updated><title type='text'>Changing Microsoft Office 2008 for Mac Splash Screen Registration Information</title><content type='html'>&lt;p&gt;I stumbled around a bit before finding this solution.  From &lt;a href="http://www.omgili.com/newsgroups/microsoft/public/mac/office/ee8ee56-1webcrossingcaR9absDaxw.html&amp;amp;q=microsoft+office+mac+2008+product+key"&gt;some post&lt;/a&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Quit all Office programs&lt;/li&gt;&lt;li&gt;Delete these files:&lt;ul&gt;&lt;li&gt;/Users/&amp;lt;user name&amp;gt;/Library/Preferences/Microsoft/Office 2008/Microsoft Office 2008 Settings.plist&lt;/li&gt;&lt;li&gt;/Applications/Microsoft Office 2008/Office/OfficePID.plist&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Start any Office program&lt;/li&gt;&lt;li&gt;Retype your name, company, and product key&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;It seems that, if you only delete one of them, it regenerates the other.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3957476534860850394?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3957476534860850394/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3957476534860850394' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3957476534860850394'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3957476534860850394'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/03/changing-microsoft-office-2008-for-mac.html' title='Changing Microsoft Office 2008 for Mac Splash Screen Registration Information'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-7014100543842429621</id><published>2008-02-14T19:49:00.001-05:00</published><updated>2008-02-14T20:20:51.472-05:00</updated><title type='text'>Memeo AutoSync is extremely slow</title><content type='html'>I just bought a &lt;a href="http://www.wdmybook.com/en/editions/"&gt;Western Digital MyBook&lt;/a&gt; &lt;a href="http://www.wdc.com/en/products/Products.asp?DriveID=357"&gt;Home Edition&lt;/a&gt; as a backup hard drive.  The drive itself is small, pleasing to the eye, and not too noisy.  It comes with WD Anywhere Backup, which itself is a rebranding of &lt;a href="http://www.memeo.com/"&gt;Memeo&lt;/a&gt; &lt;a href="http://www.memeo.com/autobackup.htm"&gt;LifeAgent AutoBackup (I assume the standard edition)&lt;/a&gt;.  This software is supposed to automatically back up your files as they are changed on your hard drive.&lt;br /&gt;&lt;br /&gt;I set the software to backup my WHOLE drive (from the C: directory down, with no exclusions).  That's about 208GB of data.  I've left it running overnight and over a day at work, and this is the progress so far.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/_yWS30lH8nfY/R7Ti-EcmLMI/AAAAAAAAABE/WHb1LiGWPbU/s400/Memeo.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;(I didn't take a snapshot of the other screen, which indicates that this process was started at 12:32 am, so it's actually been running for about 19.5 hours at this point, though it apparently was idle for a while)&lt;br /&gt;&lt;br /&gt;That's right: after 19.5 hours of work, it has only backed up about 20 GB of my data.  At this rate, it will take about &lt;b&gt;eight and a half days&lt;/b&gt; to complete.  USB 2.0 may be slow, but it ain't that slow!  The whole time, it's been using between 30% and 50% of one of my CPU cores.  &lt;br /&gt;&lt;br /&gt;To be fair, it is verifying the contents of all of the backed-up files as it goes.  I do appreciate that.  But seriously!&lt;br /&gt;&lt;br /&gt;To ensure that it wasn't a problem with the drive, I copied my 1.3GB Guild Wars data file (a single file) to the drive.  That took 1'09".  I copied my nearly 8GB Unreal Tournament 3 install to the drive (1900 files exactly), and that took 10'21".  Memeo copies files at 2% of the speed that Windows Explorer does.  Another way to look at it is that Memeo is 50x slower than Windows Explorer.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;I recommend that you don't buy Memeo software.&lt;/b&gt;  They might eventually get it right, but you should wait until then.  &lt;b&gt;Western Digital should find a new company to provide their backup software&lt;/b&gt; - this Memeo junk just makes them look bad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-7014100543842429621?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/7014100543842429621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=7014100543842429621' title='45 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7014100543842429621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7014100543842429621'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/02/memeo-autosync-is-extremely-slow.html' title='Memeo AutoSync is extremely slow'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_yWS30lH8nfY/R7Ti-EcmLMI/AAAAAAAAABE/WHb1LiGWPbU/s72-c/Memeo.PNG' height='72' width='72'/><thr:total>45</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-2555683005707095858</id><published>2008-02-10T01:54:00.001-05:00</published><updated>2009-12-22T01:22:15.348-05:00</updated><title type='text'>Removing Credit Card from Xbox Live</title><content type='html'>I accidentally charged an old credit card on XBox Live tonight.  I started looking around to figure out how to remove the old card.  Half an hour later, I stumbled upon the solution.  Since it's so obtuse, I will describe it here.&lt;br /&gt;&lt;br /&gt;Notes: You must have a Windows Live ID associated with your XBox account, and you must have another credit card tied to your XBox Live account.  You will be transferring all charges FROM one card TO another card.  This apparently also has the side effect of "removing" the old card from the system.  If you need to do something different, like to cancel XBox Live, you might need to call support instead.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Go to &lt;a href="https://billing.microsoft.com/"&gt;Microsoft Billing and Account Management&lt;/a&gt;.  Log in.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You will see a list of credit cards attached to your Windows Live ID.  Note that this will only work if you have associated your gamertag with a Windows Live ID.  (In my case, you can see that I have removed one of my cards).&lt;br /&gt;&lt;img src="http://3.bp.blogspot.com/_yWS30lH8nfY/R66hJEcmLKI/AAAAAAAAAA0/o1Tan_5aJ24/s400/SafariScreenSnapz005.png" /&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Click the card you want to delete.  This will take you to a screen that includes this:&lt;br /&gt;&lt;img src="http://2.bp.blogspot.com/_yWS30lH8nfY/R66hI0cmLII/AAAAAAAAAAk/fJ2mclpXiJE/s400/SafariScreenSnapz003.png" /&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Choose "Go to payment method information".  You will be at a screen that includes this:&lt;br /&gt;&lt;img src="http://3.bp.blogspot.com/_yWS30lH8nfY/R66hJEcmLJI/AAAAAAAAAAs/B-O-LWKotI8/s400/SafariScreenSnapz004.png" /&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Click "Use a different payment method," and select the card that you want to use.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;I could make jokes about how Microsoft is just looking for an excuse to keep your money.  Instead, I'll joke that they can't design user interfaces.  I mean, it's not like anybody else has solved the cancel-a-credit-card interface problem.&lt;br /&gt;&lt;br /&gt;Oh wait.  This is from the iTunes music store:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://2.bp.blogspot.com/_yWS30lH8nfY/R66jj0cmLLI/AAAAAAAAAA8/JOj9yfGdHB4/s400/iTunesScreenSnapz001.png"  /&gt;&lt;br /&gt;&lt;br /&gt;Of course, this should be no surprise to any 360 owner.  The 360's interface is among the worst things ever created by Redmond.  Yes, I think it's worse than &lt;a href="http://en.wikipedia.org/wiki/Office_Assistant"&gt;Clippy&lt;/a&gt;.  Quickly - where do you go to get a list of your active downloads?  If you're in a game, the only way is by pressing the "XBox" button, and selecting PERSONAL SETTINGS!  Clearly, downloads are a setting.  There are a number of settings which you change in 2 different places.  Themes are one example.  Also, the 360 is the only console whose menus are affected by the speed of your internet connection.  Quite often, I can't start a game for 30 seconds or more because the menus haven't yet loaded (presumably, downloading ads off the XBox servers).  &lt;br /&gt;&lt;br /&gt;It's worth noting that the XBox 360 has been out for OVER 2 YEARS!  The original XBox was only around for 4 years before the 360 was introduced.  This console is half dead, and the user interface is only beta quality.  Nice play, Microsoft.&lt;hr/&gt;&lt;h5&gt;Update:&lt;/h5&gt;&lt;p&gt;I noticed that Microsoft added an item to their help center in the XBox interface. You can instead go to &lt;a href="http://xbox.com/accounts"&gt;xbox.com/accounts&lt;/a&gt; to manage your credit cards. The interface is a little nicer, though they still dump you back to the old system to transfer your XBox Live subscription from one card to another.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-2555683005707095858?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/2555683005707095858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=2555683005707095858' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2555683005707095858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/2555683005707095858'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2008/02/removing-credit-card-from-xbox-live.html' title='Removing Credit Card from Xbox Live'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_yWS30lH8nfY/R66hJEcmLKI/AAAAAAAAAA0/o1Tan_5aJ24/s72-c/SafariScreenSnapz005.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-499792303839853289</id><published>2007-07-23T17:22:00.000-04:00</published><updated>2007-07-23T18:13:01.138-04:00</updated><title type='text'>The Dangers of Flash</title><content type='html'>As I sit here and use my computer (2.4 GHz MacBook Pro), I noticed that the laptop was getting quite hot.  I looked up at &lt;a href="http://81.169.182.62/~eidac/software/smcfancontrol2/index.html"&gt;smcFanControl&lt;/a&gt;, and noticed temperatures between 165 and 175 Fahrenheit.  The fans had spun up to nearly 6000 RPM (from a base of 2000).  Trying to figure out just what was going on, I noticed that Safari was using 66% of my CPU time.&lt;br /&gt;&lt;br /&gt;As I closed some tabs, I noticed that my temperature was dropping... fast.  In about 20 seconds, the temperature was down to 140-145 Fahrenheit.  the fans had slowed as well.&lt;br /&gt;&lt;br /&gt;It looks like the problem tab was &lt;a href="http://www.verizonwireless.com/chocolate/"&gt;Verizon Wireless' Chocolate Site&lt;/a&gt;.  I had clicked the commercial link because I was trying to identify the song.  I had paused the movie while I went to some other site to do some searching.&lt;br /&gt;&lt;br /&gt;I fired up &lt;a href="http://developer.apple.com/qa/qa2001/qa1236.html"&gt;Quartz Debug&lt;/a&gt;, and learned that the entire contents of the HTML document were getting redrawn continuously.  That's a lot of pixels to be pushing.&lt;br /&gt;&lt;br /&gt;So, if you ever use flash for your website, remember to take it easy on your end user's laps.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-499792303839853289?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/499792303839853289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=499792303839853289' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/499792303839853289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/499792303839853289'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2007/07/dangers-of-flash.html' title='The Dangers of Flash'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-3944351761331700257</id><published>2007-04-23T22:31:00.000-04:00</published><updated>2007-04-23T22:51:00.253-04:00</updated><title type='text'>Objects, just not Object Oriented</title><content type='html'>I've been on a real programming language kick for the past year.  First, I picked up Prolog again.  Then Javascript, then Ruby, then Lua, then Erlang.  This is in addition to lots of programming in C#, some in Java, and a little C++ on the side.  I'm starting to notice lots of similarities between languages.  I don't mean superficial similarities (Java has classes.  Whoa, so does C#.  Dude...).  I mean deeper, conceptual similarities.&lt;br /&gt;&lt;br /&gt;As an example, take objects.  C++ has objects.  Smalltalk does too.  Also Java and C# and a whole lot of other languages.  However, all of the objects in those languages are bound together by sharing a single class.&lt;br /&gt;&lt;br /&gt;Javascript and Lua don't share that requirement.  They're still object-based (you have entities which hide implementation and expose functionality), but object's don't necessarily share a common class.  In fact, an object in these languages can (and often does) have no class at all.&lt;br /&gt;&lt;br /&gt;Further still, some languages have object-like things.  Erlang, for example, allows you to create processes.  Processes can send messages to each other.  Processes can even have private data (since a process is just a function, and functions can always have local variables, processes can have private data).  Is this an object?  I think so!&lt;br /&gt;&lt;br /&gt;Some languages even give you object-like functionality in even stranger ways.  Take purely functional languages like Lisp (or Scheme) or ML.  These languages let you create "closures".  A closure is a function that exists within (is closed to) an environment.  In Javascript (not a functional language, but one which supports closures):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function make_add(left_addend) {&lt;br /&gt;  var f = function(right_addend) {&lt;br /&gt;    return left_addend + right_addend;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  //At this point, f is bound to left_addend.  Whatever&lt;br /&gt;  //we passed in as left_addend is now known by f.&lt;br /&gt;  //We can return f, and it will correctly remember&lt;br /&gt;  //the value of left_addend.&lt;br /&gt;  &lt;br /&gt;  return f;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var add10 = make_add(10);&lt;br /&gt;var add1000 = make_add(1000);&lt;br /&gt;&lt;br /&gt;print_to_console(add10(5));&lt;br /&gt;print_to_console(add10(50));&lt;br /&gt;&lt;br /&gt;print_to_console(add1000(5));&lt;br /&gt;print_to_console(add1000(50));&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;br /&gt;15&lt;br /&gt;60&lt;br /&gt;1005&lt;br /&gt;1050&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Why is this cool?  Well, we have effectively created an entity which has private data.  We could go a step further and make 'f' into some sort of dispatch method.  It could take, as its first parameter, an operation.  So, perhaps we could have this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function make_number(value) {&lt;br /&gt;  var f = function(operation) {&lt;br /&gt;  if (operation == "add") {&lt;br /&gt;    return make_number(value + arguments[1]);&lt;br /&gt;  } else if (operation == "sub") {&lt;br /&gt;    return make_number(value - arguments[1]);&lt;br /&gt;  } else if (operation == "toString") {&lt;br /&gt;    return value;&lt;br /&gt;  } else {&lt;br /&gt;    return null;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var number10 = make_number(10);&lt;br /&gt;var number1000 = make_number(1000);&lt;br /&gt;&lt;br /&gt;print_to_console(number10("add", 5)("toString"));&lt;br /&gt;print_to_console(number10("sub", 50)("toString"));&lt;br /&gt;&lt;br /&gt;print_to_console(number1000("sub", 5)("toString"));&lt;br /&gt;print_to_console(number1000("add", 50)("toString"));&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;br /&gt;15&lt;br /&gt;-40&lt;br /&gt;995&lt;br /&gt;1050&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Not too pretty, but it should work.  Object-oriented programming in functional languages.  Who knew?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-3944351761331700257?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/3944351761331700257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=3944351761331700257' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3944351761331700257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/3944351761331700257'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2007/04/objects-just-not-object-oriented.html' title='Objects, just not Object Oriented'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-4907322874814902706</id><published>2007-04-23T22:20:00.000-04:00</published><updated>2007-04-23T22:23:46.136-04:00</updated><title type='text'>Big, Bigger, Biggest Design Up Front</title><content type='html'>This was originally posted at &lt;a href="http://www.ternarysoftware.com/blogs/2007/03/12/on-windows-ce-microsoft-the-framebuffer-backwards-compatibility-and-demons/"&gt;Ternary Software&lt;/a&gt;. It is mirrored here because I wrote it and I want it on my personal blog as well as my work blog.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;h3&gt;Background&lt;/h3&gt; &lt;p&gt;Extreme Programming encourages its practitioners to do away with a technique known as &lt;a href="http://en.wikipedia.org/wiki/Big_Design_Up_Front"&gt;Big Design Up Front&lt;/a&gt;. BDUF is a software development process that has existed for nearly as long as software development. The general idea is to design a software system completely on paper before writing a line of code (experiments and proof-of-concepts aside). XP claims that this is a bad idea. In my recollection, the XP book claims that you wouldn’t get the design right anyway. You really can’t get the design right until you have finished writing the code. &lt;/p&gt; &lt;h3&gt;Reality&lt;/h3&gt; &lt;p&gt;In theory, this doesn’t sound too bad. I mean, the best way to understand a system is to try to formalize it in code. You’ll spot all of the inconsistencies and problems pretty quickly. (In fact, I think that’s the real skill that a software developer brings to the table - the ability to analyze a system and find the problems). But still, to do no design up front? Clients don’t come to you with no design. In fact, if one did, would you really take their business?&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;Prospective Client: Boy, do I have a project for you!&lt;br /&gt;Development Team: Great, what do you have in mind?&lt;br /&gt;Prospective Client: Well, see, it’s a thing. It does stuff. It’s a lot like That Web Site, only different. By different, I mean really different. But I’m not quite sure how. Also, it transforms into a boat.&lt;br /&gt;Development Team: What?  That makes no sense!&lt;br /&gt;No Longer Prospective Client: Well, I thought I’d design it as I go.&lt;br /&gt;Development Team: Well, we’re really booked right now, but we’ll call you as soon as we have some free time.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;So I suspect that developers, too, need to do some design up front. I can think cases where NOT designing up front has cost us a lot of effort in re-design. Developing a 2-tier system when we knew that the client really wanted a 3-tier system is but one example. (We fixed that problem within a month, but it was still a hassle that could have been avoided). But how much design is too much? The XP book says something about doing “enough” design, but that doesn’t really provide a lot of guidance.&lt;/p&gt; &lt;h3&gt;Disclaimer&lt;/h3&gt; &lt;p&gt;Now we get into the part that I don’t really understand well enough. These are my thoughts, but they’re surely wrong. My thoughts will probably change with time, and I would appreciate any feedback that I can get.&lt;/p&gt; &lt;h3&gt;3 Kinds&lt;/h3&gt; &lt;p&gt;Here’s an over-generalization: systems that you design can fall into 3 categories:&lt;/p&gt; &lt;ol&gt;&lt;li&gt;Systems that perfectly implement the requirements of today with no thought to the future. Any attempt to use them in a way that is different from the original intent will require massive amounts of redesign. I’ve seen a lot of Javascript code that seems to fall into this category. For that matter, a lot of code written on consulting projects seems to fall into this category.&lt;/li&gt;&lt;li&gt;Systems that implement any conceivable requirement for the next 5 years. Sometimes, they do a good job of this; sometimes, they fail miserably. Yes, ZZZManager, I’m talking about you! A lot of libraries tend to lean in this direction.&lt;/li&gt;&lt;li&gt;Systems that, while implementing the requirements of today, also have a lot of hooks for the requirements of tomorrow. The assumptions that they make (and the hooks that they provide) aren’t necessarily perfect, but it’s not TOO hard to change them to support new requirements. Some really good libraries and really good frameworks do this. When I saw how simple it was to write a Java Servlet, I remember thinking that Servlets strike an excellent balance of providing functionality to the programmer without making too many assumptions.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;I would furthermore attempt to characterize those three kinds of designs as:&lt;/p&gt; &lt;ol&gt;&lt;li&gt;Not Enough Design&lt;/li&gt;&lt;li&gt;Too Much Design&lt;/li&gt;&lt;li&gt;A design that’s juuuuust right.&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;The Source of All Design that is Good&lt;/h3&gt; &lt;p&gt;Let’s assume that I’m correct in my characterization. How do you get this kind of design? Well, I think it’s a little like Chess. For chess masters, the match is won by the person who can “see” further into the future. I think that good software design comes from people who can (correctly) guess the future. There are a number of contributing factors:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;You yourself. I think some people just naturally think more about the future than others. I don’t think that people who think about the future are better at designing (see Too Much Design). But I do think that it’s important to be thinking about both the present and the likely future.&lt;/li&gt;&lt;li&gt;Your experience. If you’ve seen a problem before, there’s a chance that you will try to avoid it in the future. I think that experience can help you avoid problems without even realizing that you are avoiding them - you just make really good choices.&lt;/li&gt;&lt;li&gt;The pressures on your team. I think teams that have some healthy amount of slack time will be more likely to think about things that are a little broader than their current task.&lt;/li&gt;&lt;li&gt;Your level of attunement. The more in-tune you are with your client and their needs, the more likely you are to guess correctly about their future requirement needs. You need to understand not only their product, but to a certain extent, their business model and their users.&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;Tips&lt;/h3&gt; &lt;p&gt;Those factors are often hard to change. Are there more immediate guidelines for avoiding problems? I certainly don’t know how to avoid all problems, but here are some general thoughts that have helped me so far:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;If, while implementing a feature, you think you will be ripping out the code in a month, you probably need to design a little more. Changing code in a month is OK, but replacing it is scary. Replacing it in 3 years is probably OK.&lt;/li&gt;&lt;li&gt;If you can’t explain to the client what you are working on and why they need it, you’ve probably designed the wrong thing (i.e. over-design)&lt;/li&gt;&lt;li&gt;Don’t take shortcuts. If there’s an easy way and a hard way to design a system, make sure that the easy way isn’t really the “really hard later” way. It might be perfectly fine to take the easy approach. In fact, you probably should. Just make sure that you’re not going to get screwed in the near future.&lt;/li&gt;&lt;li&gt;If you are writing very similar (but also slightly different) code in several places, you probably need more design. For example, you use an Excel writing library in 20 places in the code base, something is screaming to be extracted into its own class (or set of classes).&lt;/li&gt;&lt;li&gt;If your code has no layers, you probably need to design some more. Just as classes need to be small and simple, so too must groups of classes. If you have a class that is referenced by code in several namespaces, you should wonder why everybody needs to know about him. (Perhaps everyone really does need to know, but you should still think about it).&lt;/li&gt;&lt;li&gt;If you end up writing a lot of static methods, you need to extract some classes. A few static methods (especially named constructors) are OK, but if you have too many, you’re just writing procedural code.&lt;/li&gt;&lt;li&gt;If you end up writing a lot of private methods, you need to extract some classes. A few private methods are OK, but if you have too many, you’re just writing procedural code.&lt;/li&gt;&lt;li&gt;If you know that some of the code that you are writing is more general than your specific task, break it into 2 pieces. Write some of the code in a general way (using &lt;a href="http://en.wikipedia.org/wiki/Template_method_pattern"&gt;Template Method&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Object_composition#Aggregation"&gt;aggregation&lt;/a&gt;, or &lt;a href="http://en.wikipedia.org/wiki/Closure_%28computer_science%29"&gt;something cooler&lt;/a&gt;), and write your specific code in terms of the general pattern. LISPers (and other functional programming dudes) know how to take this to the extreme. (Every functional language that I’ve encountered comes with a built-in function that can apply a function to every element of a list. Finding the sum of a set of numbers is often as simple as applying “+” to every element of a list). Be very wary of this, however, as this can lead to some scary over-design.&lt;/li&gt;&lt;li&gt;If you don’t know what you are going to be doing later this week, you can’t anticipate the future.  Have a plan.&lt;/li&gt;&lt;li&gt;If your plan describes what you will be doing for 6 months, you will be using a different plan in 6 months. You’ll probably write code that won’t make sense in that new plan. Spend less time designing, and focus on short, iterative releases.&lt;/li&gt;&lt;li&gt;If you write code that everybody uses, and which affects code throughout the system, and your code needs to be changed whenever some other requirement changes, you have too much centralization. You’ve designed a fancy system, but it’s going to cause you problems.&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;Conclusion&lt;/h3&gt; &lt;p&gt;Those are all of my ideas for now. Actually, I could keep going, but I would just be rambling more. I want to again stress that this area is very unclear to me, and what I have typed here are only my current thoughts on the subject. I would love to hear other people’s opinions!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-4907322874814902706?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/4907322874814902706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=4907322874814902706' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4907322874814902706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/4907322874814902706'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2007/04/this-was-originally-posted-at-ternary.html' title='Big, Bigger, Biggest Design Up Front'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-7563961104648036751</id><published>2007-04-23T22:10:00.000-04:00</published><updated>2007-04-23T22:23:16.985-04:00</updated><title type='text'>On Windows CE, Microsoft, the framebuffer, backwards compatibility, and demons</title><content type='html'>This was originally posted at &lt;a href="http://www.ternarysoftware.com/blogs/2007/03/12/on-windows-ce-microsoft-the-framebuffer-backwards-compatibility-and-demons/"&gt;Ternary Software&lt;/a&gt;. It is mirrored here because I wrote it and I want it on my personal blog as well as my work blog.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;I spent the better part of today struggling to get some fancy graphics on my &lt;a href="http://www.gsmarena.com/o2_xda_exec-1279.php"&gt;O2 XDA Exec&lt;/a&gt; (read a &lt;a href="http://www.coolsmartphone.com/article499.html"&gt;review&lt;/a&gt; if you are interested in the device).  I know that I can use the &lt;a href="http://www.microsoft.com/technet/prodtechnol/wce/evaluate/graphics.mspx"&gt;Windows CE GDI&lt;/a&gt; to draw the graphics on the screen. I also know that will be slow. Not necessarily too slow, but certainly not an API that any self-respecting game developer would use. So, moving along, I knew that there was the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms837924.aspx"&gt;GAPI&lt;/a&gt; - the Game API. It sounded great - provide access to the framebuffer and to low-level keyboard functions. So I read and implemented. &lt;p&gt;Everything was working fine in the Pocket PC emulator. But on a real device, no luck. It ran, but incorrectly. For one, it pretended that my device had a resolution of 240×320 pixels, which is 1/4 of the actual number. It used pixel doubling to fill the whole screen. On top of that, it wouldn’t draw anything in the leftmost pixel column. I had an ugly blue column extending from the top of the screen to the bottom.&lt;/p&gt; &lt;p&gt;I spend hours looking for a solution. I looked to see if it was a clipping problem, an API misunderstanding, a device issue. I couldn’t find any information. Then, I started reading support forums for other applications. It was there that I discovered that the problem is that, at this point, GAPI is provided on new devices as an emulation library. Apparently, in the Pocket PC 2002 / Windows Mobile 2003 days, GAPI was &lt;strong&gt;the&lt;/strong&gt; way to make games. However, a lot of game developers made a lot of assumptions about transient things like, say, the screen resolution of devices. Microsoft, fearing that high resolution screens would break a lot of existing applications, decided to release a GAPI implementation for more recent devices that doesn’t return a pointer to the ACTUAL framebuffer. Instead, the application gets a pointer to a back buffer. Upon calling GXEndDraw(), a GAPI function whose purpose should be reasonably obvious (it flags that drawing operations are complete), this emulation library flipped the buffers and (on my device), upscaled all of the pixels.&lt;/p&gt; &lt;p&gt;HOWEVER, certain crafty game developers apparently thought that GXEndDraw was a useless function. After all, it didn’t seem to do anything on their Windows Mobile 2003 devices. And, if it doesn’t do anything, why should they take the time to call it? I believe that this is why I was having trouble with &lt;a href="http://quake.pocketmatrix.com/"&gt;Pocket Quake&lt;/a&gt;.  I plan to try to fix it and then tell the developer.&lt;/p&gt; &lt;p&gt;As for the single unwritable column of pixels, I simply think that this is a bug in the copy of gx.dll (the GAPI runtime library) that is on my device. The pixel doubling routine is probably wrong. I believe this because I think I have solved my problem. I can write all 480×640 pixels, including that annoying leftmost column. &lt;/p&gt; &lt;p&gt;To perhaps save somebody else the time that I just spent, let me share my findings. Microsoft suggests using the GETRAWFRAMEBUFFER escape operation in &lt;a href="http://msdn2.microsoft.com/en-us/library/ms838191.aspx#dpi_awareness_topic4"&gt;the GAPI Legacy Support section of “Developing DPI-Aware Applications”&lt;/a&gt;. There’s a good example in that article. This seems to be the new way to really, definitely, for sure this time, get raw access to the framebuffer. Until some developer takes too many liberties with it, and Microsoft has to invent yet another hack to keep other people’s applications working.&lt;/p&gt; &lt;p&gt;I should probably just bite the bullet and use DirectDraw.  I thought that I had read on the &lt;a href="http://www.wincesoft.de/html/gapi_for_hpc_s.html"&gt;GAPI for Handheld PC’s&lt;/a&gt; page that DirectDraw was not supported on the PocketPC platform, but they were perhaps referring to the 2002/2003 platform. I don’t see DirectX dlls on my device, but perhaps they are hiding.&lt;/p&gt; &lt;p&gt;Perhaps I’ve read so many of &lt;a href="http://blogs.msdn.com/oldnewthing/"&gt;Raymond Chen’s posts&lt;/a&gt; that I’ve started to sound a little like him.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-7563961104648036751?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/7563961104648036751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=7563961104648036751' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7563961104648036751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/7563961104648036751'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2007/04/on-windows-ce-microsoft-framebuffer.html' title='On Windows CE, Microsoft, the framebuffer, backwards compatibility, and demons'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-116184199457132834</id><published>2006-10-26T01:49:00.000-04:00</published><updated>2006-10-26T02:37:10.416-04:00</updated><title type='text'>The Double If</title><content type='html'>This is a anti-pattern that I have started to see when people attempt to think in terms of objects, but don't think in terms of messages.  The case is that you have one bit of code that will do a test and will return either a real value or null (in this case, that method is GetParentName).  Then, anybody who uses GetParentName needs to either be OK with getting a null back, or needs to test to make sure that the value returned is not null.  This code illustrates that anti-pattern:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    class Item&lt;br /&gt;    {&lt;br /&gt;        //Return either a real string or null&lt;br /&gt;        string GetParentName()&lt;br /&gt;        {&lt;br /&gt;             Item parent = GetParent();&lt;br /&gt;             &lt;b&gt;if (parent == null)&lt;/b&gt;&lt;br /&gt;             {&lt;br /&gt;                 return null;&lt;br /&gt;             }&lt;br /&gt;             else&lt;br /&gt;             {&lt;br /&gt;                 return parent.GetName();&lt;br /&gt;             }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void SetupTextBox(Item myItem, TextBox someTextBox)&lt;br /&gt;    {&lt;br /&gt;        string parentName = myItem.GetParentName();&lt;br /&gt;        &lt;b&gt;if (parentName != null)&lt;/b&gt;&lt;br /&gt;        {&lt;br /&gt;            //TextBox::SetText(string) will throw an exception&lt;br /&gt;            //if it is passed a null&lt;br /&gt;            someTextBox.SetText(parentName);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A way to fix that is to change the class' interface.  Instead of getting values from the class, let's tell the class to tell its information to somebody.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    class Item&lt;br /&gt;    {&lt;br /&gt;        string TellParentNameTo(StringReceiver receiver)&lt;br /&gt;        {&lt;br /&gt;             Item parent = GetParent();&lt;br /&gt;             if (parent != null)&lt;br /&gt;             {&lt;br /&gt;                 receiver.SetString(parent);&lt;br /&gt;             }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void SetupTextBox(Item myItem, TextBox someTextBox)&lt;br /&gt;    {&lt;br /&gt;        //myItem will do the best that he can do, even if he &lt;br /&gt;        //has a null&lt;br /&gt;        myItem.TellParentNameTo(someTextBox);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The biggest problem with the second example is that you need to have really, REALLY broad interfaces that are used everywhere.  In this example, TextBox would need to be a StringReceiver.  In fact, anybody that could conceivably get a string from an Item would need to be a StringReceiver.  If TextBox were written so that it is a StringReceiver from the beginning, everything works great.  However, suppose that StringReceiver was written later than TextBox.  Either TextBox will need to get it retrofitted, or an adapter class will need to be written:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    class Item&lt;br /&gt;    {&lt;br /&gt;        string TellParentNameTo(StringReceiver receiver)&lt;br /&gt;        {&lt;br /&gt;             Item parent = GetParent();&lt;br /&gt;             if (parent != null)&lt;br /&gt;             {&lt;br /&gt;                 receiver.SetString(parent);&lt;br /&gt;             }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    class TextBoxStringReceiverAdapter&lt;br /&gt;    {&lt;br /&gt;        TextBoxStringReceiverAdapter(TextBox textBox) {}&lt;br /&gt;&lt;br /&gt;        void SetString(string str)&lt;br /&gt;        {&lt;br /&gt;            textBox.SetText(str);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void SetupTextBox(Item myItem, TextBox someTextBox)&lt;br /&gt;    {&lt;br /&gt;        //myItem will do the best that he can do, even if he &lt;br /&gt;        //has a null&lt;br /&gt;        myItem.TellParentNameTo(&lt;br /&gt;            &lt;b&gt;new TextBoxStringReceiverAdapter&lt;/b&gt;(&lt;br /&gt;                someTextBox)&lt;br /&gt;            );&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-116184199457132834?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/116184199457132834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=116184199457132834' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/116184199457132834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/116184199457132834'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2006/10/double-if.html' title='The Double If'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-32241238.post-116011185980580195</id><published>2006-10-05T23:16:00.000-04:00</published><updated>2006-10-26T02:17:39.833-04:00</updated><title type='text'>Concurrency</title><content type='html'>There has been a lot of talk recently about how the exponential growth in CPU power is tapering off.  Almost everybody is saying that the future will be multicore processors and multiprocessor systems.  A lot of consumer PCs are shipping with dual core processors.  &lt;a href="http://en.wikipedia.org/wiki/Xbox_360#Central_Processing_Unit"&gt;The Xbox 360 uses a custom, triple-core CPU&lt;/a&gt;.  &lt;a href="http://en.wikipedia.org/wiki/Playstation_3#Hardware_summary"&gt;The PlayStation 3 will have 7 active cores&lt;/a&gt;.  This isn't theory; it's quickly becoming fact.&lt;br /&gt;&lt;br /&gt;Everybody who has ever tried knows that multithreaded programming is hard. The programmer is required to do a lot of mental juggling to try to imagine what the system will do.  I'm not a fan of making a programmer do a lot of mental gymnastics if the computer can do a better job of it.&lt;br /&gt;&lt;br /&gt;One of the biggest challenges in multiprocessing is in dividing an application into multiple threads.  To this day, most applications run in a single thread.  Even when an application has multiple threads, it's usually only when performing some lengthy operation, such as a find.  If multiple threads were not used, the entire application would freeze up.&lt;br /&gt;&lt;br /&gt;Interestingly, programmers have been dividing their programs into pieces for a long time.  Instead of carrying out a process by performing 1 million discrete operations, we perform 10 high-level operations.  These operations need to be carried out in order, because the output of one is used as the input to the next.  Also, each of those high-level operations is implemented in terms of 15 mid-level operations, and each of those is implemented in terms of another 10 mid-level operations, and so on.  These subdivisions are usually used to take what is essentially one long process and split the code for that process into multiple, bite-sized chunks that an individual programmer can understand.&lt;br /&gt;&lt;br /&gt;Unfortunately, This doesn't help with the concurrency problem.  However, a lot of programmers have started programming in an object-oriented way.  Making classes is a very different process from making functions.  The purpose of functions is allow the programmer to focus on a small section of code.  However, the code that the programmer is working on is still sitting inside of some larger whole.  The purpose of classes is to let the programmer work on a piece of the program in complete isolation.  This is why a class contains both state and behavior.  In fact, the class that a programmer writes for one program should be able to be dropped, without change, into a completely different program.  A BlogPost class should be reusable, as long as it provides all of the behavior that everybody needs.&lt;br /&gt;&lt;br /&gt;Good OO design is characterized by a principle that I recently learned.  The principle is "Tell, don't ask".  This has nothing to do with bullying your objects around.  To translate, to "tell" your objects is to send messages to them.  To "ask" your objects is to write them such that they cough up their internal state to whomever asks.  So, the principle is really "write your objects so that they expose behavior to the outside world, but don't write them so that they expose state to the world".  I'd love to go into more detail, but I don't have space right now.&lt;br /&gt;&lt;br /&gt;If we take TDA to the extreme, we can make some radical assumptions.  First of all, we can completely remove return values.  Return values, I claim, are simply an artifact of the procedural days.  If you need to have an object tell you something, send a callback object to him.  You TELL him to TELL you something.  Crazy, no?  Now, you might point out that this is a lot of work for something that used to be handled by something as simple as a return value.  This is a valid concern.  However, my gut tells me that the callback scheme would actually simplify a lot of code.  Again, I would love to go more into this topic, but I don't have space.  Please just assume (as I am assuming) that callbacks are preferable to return values in a lot of cases.&lt;br /&gt;&lt;br /&gt;So, with this mindset, we can really think of method calls on an object instead as sending messages to the object.  Since the object can't send anything back (except by sending US a message), there's no guarantee that the target object will process the message right away.  In fact, there's no guarantee that the target object will process the message at all.  For example, when you're working on a networked application, you need to realize that nothing is ever guaranteed.  You might have an object that represents a remote computer.  You might send a SendPacket message to that object, but that object might not send a packet out right away.  In fact, if the remote computer goes down, the packet might never get sent at all.&lt;br /&gt;&lt;br /&gt;Most importantly, this shifts our thinking.  In the procedural world, it is vital that all steps be carried out in the correct order.  This is because each step will update data somewhere and return information to the caller.  In the TDA world, this can never happen.  Of course, SOME ordering is still needed.  But that ordering can be captured in a different way.  Only the significant order is enforced.&lt;br /&gt;&lt;br /&gt;A trend that I am starting to see in systems that are designed with TDA is that they seem to be "configured" more than "programmed".  I mean that main() is pretty much responsible for instantiating a bunch of instances and wiring them up correctly.  Then, main() sends one message to one object and, like a bunch of dominos, eventually something comes out of some other object.  In the meantime, all of the objects involved were furiously chatting.  The important thing is that they were all basically dormant until a message was sent to them.  Then, they did a whole lot of stuff and sent a whole lot of messages to other objects, but then they went dormant again.&lt;br /&gt;&lt;br /&gt;So, what does all of this have to do with concurrency?  Suppose that each object in the system had its own thread and message queue.  The queue is pumped by the thread, and each incoming message is dispatched to the appropriate message handler.  Now, when an object needs to collaborate with other objects, it not only doesn't HAVE to wait until the other object is done, it simply DOESN'T wait.  It is executing in a completely different thread that may be running on a different processor (or even a different machine!).  Also, since the object doesn't have to wait, it can run through all of its code very quickly and return to a dormant state.  While dormant, the object's thread would not get scheduled.  &lt;br /&gt;&lt;br /&gt;What this effectively does is to turn a processor's execution cores into a simple resource, like physical ram.  Just as a program does not need to decide where in physical ram to store its data (it stores its data in virtual address space, which the processor maps to physical ram), the program will ALSO not need to worry about how many threads it has.&lt;br /&gt;&lt;br /&gt;There are so many details that I have left out.  This post could turn into a whole lot of smaller posts with a lot of detail about various small parts of this scheme.  But this is a decent overview.  I hope that people read this and have good ideas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/32241238-116011185980580195?l=bytecrafter.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bytecrafter.blogspot.com/feeds/116011185980580195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32241238&amp;postID=116011185980580195' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/116011185980580195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32241238/posts/default/116011185980580195'/><link rel='alternate' type='text/html' href='http://bytecrafter.blogspot.com/2006/10/concurrency.html' title='Concurrency'/><author><name>Daniel Yankowsky</name><uri>http://www.blogger.com/profile/06099373265709774874</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='30' src='http://3.bp.blogspot.com/-GTPy-xNKvQw/TmWEoPDX3dI/AAAAAAAAATE/CBC8fU_vYjo/s220/IMG_1300.png'/></author><thr:total>2</thr:total></entry></feed>
