Thursday, June 26, 2008

Planescape: Effortless

I was amazed to learn that Planescape: Torment, 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 fervent dedication to backwards compatibility, nVidia's seemingly rock-solid drivers, and Black Isle's crack team of developers made this possible.

Recently, Scott Hanselman interviewed Steven Frank of Panic (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.

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.

Of course, now we have virtualization, so maybe it's not such a big problem.

On Succinctness

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?

On Usability

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 Expose. It's such a simple but powerful concept. It's also made for a great demo. I hate Flip 3D. It's pretty, but also pretty pointless.

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.

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 isBranch() and hasChildren(). What's the difference? Should they ever be different? Who knows?!

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 Apple Design Award 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?"

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.

If you can't make an API grokkable, at least document it well. The standard Java and MSDN docs are full of nonsense.

setFoo(IFurple f) - sets foo to f

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.

Monday, June 23, 2008

Vista Usabaility... It's Got Issues

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.

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.


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?
Can you believe that it's easy to delete the wrong file?


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.


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.


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:

Wait, I think maybe there's a bit more to it...

Ah, I bet we can expand that.

There we go!
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.

Search results, you say. Search results, indeed. And people are trying to emulate it! Little hint: just because Microsoft does it, doesn't mean it's a good idea.

Even the Windows Classic theme isn't safe. The "expandos" in the classic TreeView fade out too.


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.

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 seven 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.

I can only imagine what it must have been like to have seen this live:

... especially when this is what people were used to...

Wednesday, June 18, 2008

Flex Builder 3 Debugger on Mac OSX

I was having a hard time getting Flex Builder on my Mac to behave. I came across a post at Big Spaceship Labs 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.

This technique is useful for keeping several Flash Players on your system (or anything, really). I think I read that MultiSafari 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.

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.

Sunday, June 08, 2008

Exceptions in Multithreaded Programming

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.

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:

class WorkItem implements Runnable {
@Override
public void run() {
try {
//do some work
} catch (SomeException e) {
//WHAT DO WE DO HERE?
}
}
}

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.

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

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.

Wednesday, June 04, 2008

Aspect Oriented Programming in Javascript

Working with YUI today, I found that I wanted to augment a built-in method. I am using a custom subclass of TreeView. 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 Node base class doesn't do this.

This sounded like the sort of thing that could be solved using Aspect Oriented Programming. 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.

function augment(obj, fn, options) {
var original = obj[fn];

//make copies of the fields in options in case it changes
//between now and when the generated method is called
//(which may be a long time).
var before = options.before;
var replace = options.replace;
var after = options.after;

obj[fn] = function() {
if (before) {
before.apply(this, arguments);
}

if (replace) {
//TODO: we should probably send original along with
//this call, possibly embedded in arguments
replace.apply(this, arguments);
} else {
original.apply(this, arguments);
}

if (after) {
after.apply(this, arguments);
}
}
}


//samples:
augment(YAHOO.widget.TreeView.prototype, "draw", {
after:function() {
this.nodeRefreshed(this.getRoot());
}
});

augment(YAHOO.widget.Node.prototype, "refresh", {
after:function() {
this.tree.nodeRefreshed(this);
}
});

//also needed (for those playing along at home):
YAHOO.widget.TreeView.prototype.nodeRefreshed = function() { };

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 apply(), which allows us to be ignorant of the parameters that the original function takes. In the sample, I also register a dummy implementation of nodeRefreshed() so that I don't need to perform a null check in my augmented refresh().

One improvement that I would like to make, but is probably impossible, is to make the environment for replace() 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. I know you can do it in Ruby. 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).

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. I've ranted against that before. 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.

I hope to post soon about my experiences with YUI's TreeControl, Drag and Drop library, and other components.