Sunday, 1 February 2009

Das Unheimliche

Last weekend I went for a walk along the Bodensee. It was a beautiful sunny crisp winter day and people were out in force, catching whatever warmth they could from the sun before it disappeared.

All of a sudden I was struck by the feeling of being an outsider walking through somebody else's world. This was a strange feeling for someone who had just spent six months living in china: I mean in Germany I look like everyone else; I dress like everyone else; I even (arguably!) speak the language.

One of our major frustrations in China was knowing that, no matter how long we lived there, we would always look like foreigners. We could live there for 30 years, speak fluent Manadarin with no trace of an accent, and still pay more at the market than a "local". We were treated differently (though, in many cases, for the better) and yet, at some level, we fit in. While the pigeonholing was frustrating, our role in society was "expat" and playing it was pretty straightforward.

Contrast that to (relatively small-town) Germany: here I'm not given the role of "foreigner". Here I look pretty much like everyone else so, instead of being a convincing expat, I sometimes feel like an unconvincing German. This reminded me of a blog post by Jeff Atwood from a while back about the Uncanny Valley hypothesis. This hypothesis relates to all kinds of things from robotics to animation and (Bill Higgins says) web-based user interfaces. The idea is that, as one thing gets closer to mimicking something else, the remaining differences make us feel increasingly uncomfortable. So, for example, an animated character (like the one above, from Polar Express) that is trying to look human may feel creepy but one that looks nothing like a real human (Homer Simpson, for example) may feel totally natural. By the way, if you haven't seen it, check out how far computer generated animation and motion capture can currently take us: meet Emily.

I'm off to practice being a more realistic German...

Saturday, 24 January 2009

GLASS workshop

Cool... this place is offering a week-long workshop on Seaside with GLASS. The page is a bit confusing and light on details but it's cool that this kind of thing is being offered.

Monday, 19 January 2009

The Year of Seaside?

I'm a few weeks late, but I just finished listening to James Robertson and Michael Lukas Smith doing the year in review on the Industry Misinterpretations podcast. I was amused to hear them use the phrase "the year of Seaside" (a reference to Randal Schwartz's predication for Smalltalk this year). There did seem to be a lot of Seaside-related news.

But now that 2008 is over, was it the year of Smalltalk (or Seaside)? Sure, there's a lot of Smalltalk code being written, but that's been true for as long as I've been involved in the community. We made some progress with Gartner; attendance at Smalltalk conferences was high (though it sounds like there are fears it could be low again next year); Randal's predication generated a buzz in the community; and certainly there has been a notable effort to generate publicity outside the community. But is it working?

Have people seen an increase in the size of the community? An increase in Smalltalk-related business? What are our metrics? Was 2008 The Year of Smalltalk?

Tuesday, 13 January 2009

The Socratic Method

While cleaning out my browser's bookmarks (yes, I'm obsessive enough to do this once and a while), I stumbled across an article I had read by Rick Garlikov about teaching children using the Socratic Method. The Socratic Method, used extensively in the portrayal of Socrates in Plato's writings, is a form of philosophical exploration using questions to stimulate discussion and insight.

In this context, Garlikov is attempting to teach a third grade class about binary arithmetic by asking only questions and allowing the children to work out the answers themselves. A major part of the article is a transcript of this class, which lasted only 25 minutes and apparently resulted in 19 out of 22 students having "fully and excitedly participated and absorbed the entire material".

The article is a quick and inspiring read and I suggest you take a look. I sometimes think it would be nice to volunteer to work with a group of school children (eToys maybe?) and this would be an interesting approach to play with.

There is also a letter to his daughter with further details on Plato and the Socratic Method. If you are at all interested in pedagogy or philosophy, you might also want to check out some of his other articles. I seem to recall finding the article about mistakes made when teaching math interesting.

Monday, 12 January 2009

First Sprint of 2009

This weekend, we completed another very successful Seaside sprint here in Konstanz. We polished off all but a few of our targeted issues and made real progress towards the next alpha release.

Among other things, we added the ability to configure the new session cache, removed our dependency on MessageSend, standardized on the ANSI exception handling protocol and made sure we were signaling meaningful errors.

We also got a shiny new Control Panel implemented in OmniBrowser (see image to the right). There will be more features coming in this area.

Lukas was cleaning up the FileLibrary code on the train on the way here but lost most of it when his image suddenly crashed and ended up only 4MB in size.

I still need to look at some package dependency issues and Philippe and Lukas are working on the ability to add cookies during the callback phase and related bugs.

All in all, a very productive two days!

Wednesday, 7 January 2009

Object Initialization

Making sure your Smalltalk objects are initialized once and only once can be tricky.

(if you'd rather skip the discussion and go straight to the details, here are Seaside's object initialization conventions)

For starters, some Smalltalks implement #new on Object to call #initialize and some don't. But it gets more complicated than that. For example:
  • What should you call a new parameterized initialization method?
  • How should a subclass with one of those make sure its superclass is initialized?
  • How should class-side instance creation methods work?
  • How do you make sure inherited instance creation methods don't result in partially initialized objects?
  • What if someone else has made subclasses of yours and is already overriding your superclass' initialization method?
These are just a handful of the possible issues.

For much of the code you write, you can probably avoid worrying about this too much. You were probably using and testing the code as you wrote it, so you know it works. You probably don't have that many levels of subclasses and you (or at least someone on your team) probably knows (or can trace) all the users and subclasses of each class. In many cases, it probably isn't even the end of the world if an object is initialized twice and you're bound to notice pretty quickly if an object is never initialized. All this breaks down as your project and team get bigger, of course.

As a framework, though, I believe Seaside needs to hold its developers to the very highest possible standards of code quality. Seaside is used in Smalltalk images of various platforms all over the world and there is no way that any developer will ever be aware of all the subclasses and users that are out there. Some of our users will implement classes where it does matter if they're initialized twice and when it breaks it will be them not us who notices the problem. This means that we need to be unambiguous, clear, concise, consistent, and portable (and yes, documented... sigh), as well as vigilant in thinking about how developers will subclass, extend, and use our code.

For Seaside, Avi and I worked out a loose convention for this from day one and it has been fairly consistently applied. Some recent discussion on the mailing list, however, has prompted me to formalize and document our conventions. Our requirements are something like:
  1. Each object must be initialized once and only once.
  2. During initialization, all initialization methods must be called in a predictable order. If my subclass has already overridden my superclass' initialization method, it should still be called. (This means a method should never call super with a selector other than its own).
  3. All inherited instance creation methods must result in a completely initialized object.
  4. The conventions must be consistently applicable in all cases.
  5. It should be very clear to users of the class what parameters are required for initialization.
  6. It is more important to minimize the complexity and effort for users of the framework than for developers of the framework.
  7. Without sacrificing the above points, we should not have to write or override more code than necessary (we don't want to have to override every instance creation method each time we subclass, for example).
So here are the conventions we use in Seaside. Let me know what you think.

Monday, 29 December 2008

Seaside 2.9: Exception Handling

Ok, I promised well over a month ago to start documenting some of the new bells and whistles in the Seaside 2.9 Alpha series. I thought I'd start things off with a discussion of the new exception handling mechanism.

Have you ever wanted to customize the look of Seaside's error pages? Want to send yourself an email whenever an exception is raised? Maybe save a copy of the image on errors so you can look at the stack later? It has been possible for ages to implement a custom error handling class for your Seaside application but the process was not necessarily obvious and you were limited to catching subclasses of Warning and Error. In the upcoming Seaside release, we have cleaned up the exception handling code to really simplify the error handlers that come with Seaside and to make your custom handlers simpler and more flexible.

Basic Usage

To create your own exception handler, the first question is what class to use as your superclass. If you are just interested in customizing the appearance of error pages or performing some additional action when an error occurs, you probably want to subclass WAErrorHandler. If your requirements are more complex, you may want to subclass WAExceptionHandler directly (see Advanced Usage below).

Subclassing WAErrorHandler

WAErrorHandler is already configured to handle Error and Warning and is a good starting point if you don't need to drastically change the error handling behaviour. In your custom subclass, you can implement #handleError: and #handleWarning: to define the behaviour you want in each case. The methods take the exception (either an Error or Warning respectively) as a parameter and should ultimately either resume the exception or return an instance of WAResponse. By default, both methods call #handleDefault: so you can provide common implementation there.

For example, assuming you had defined a routine to notify yourself of errors by email, you could provide an implementation like this:

handleError: anError
self notifyMeOfError: anError.
^ WAResponse new
internalError;
contentType: WAMimeType textPlain;
nextPutAll: 'Eek! An error occurred but I have been notified.';
yourself

Returning an HTML Response

You could of course provide an HTML response but you would want to make sure your HTML is valid (and that means a head, a title, a body, etc.):


handleError: anError
^ WAResponse new
internalError;
nextPutAll: '<html>
<head><title>Error!</title></head>
<body><p>An error occurred</p></body>
</html>';
yourself

If you want to use the Canvas API, you can subclass WAHtmlErrorHandler instead. Just implement #titleForException: and #contentForException:on: (it gets passed the exception and a canvas) and you're done.

Using Your Exception Handler

Once you have written your exception handler, you need to configure your application to use it. Open your web browser and navigate to the Seaside configuration page for your application. There you will find a preference called 'Exception Handler' and you should be able to choose your new class from the list.

Alternatively, from a workspace, execute something like:

(WAAdmin defaultDispatcher entryPointAt: 'myapp')
preferenceAt: #exceptionHandler put: MyHandlerClass

Advanced Usage

If your needs are more complex than the above, you may want to subclass WAExceptionHandler directly. An exception handler needs to do two things:

  1. choose which exceptions to handle
  2. define how to handle them

Selecting Exceptions

The easiest way to select which exceptions to handle is to implement #exceptionSelector on the class-side of your new handler (note: in Seaside 2.9a1 this method is named #exceptionsToCatch). Your #exceptionSelector method should return either an ExceptionSet or a subclass of Exception. You probably want to continue handling the exceptions handled by your superclass so your implementation might look like:

exceptionSelector
^ super exceptionSelector, MyCustomError

If your needs are somehow more complex, look at implementing #handles: and #, yourself on either the class- or instance-side, as appropriate (note: these two methods do not exist in Seaside 2.9a1).

Handling Exceptions

When an exception is signaled and your handler indicates (via #handles:) that it wishes to handle the exception, #handleException: is called and passed the signaled exception. To define your exception handling behaviour, simply implement #handleException: on the instance-side of your handler. Note that the same handler instance is used throughout a single HTTP request even if multiple exceptions are signaled.

The #handleException: method is expected to return a WAResponse object. It may also choose to resume the exception, cause the Request Context stored in its requestContext instance variable to return a response (by using #redirectTo: for example), or otherwise avoid returning at all; but if it returns, it should return a response.

Internal Errors

WAExceptionHandler also provides a class-side method called #internalError:context:. This method creates a new instance of the handler and calls #internalError:, which should generate a very simple error message. This method is used whenever the exception handling mechanism itself signals an error as well as any other place in the system where we cannot be certain that a more complex error handler will succeed. This method should not do anything that has potential to raise further errors.

Setting Up an Exception Handler

Exception handling for Seaside applications is set up by WAExceptionFilter (more information on filters in a later post) and you do not need to do anything else if you are using WAApplication and WASession. If you are implementing your own request handler, however, and want to use the exception handling mechanism, you will need to set up an exception handler yourself.

Although you could do everything yourself, the easiest thing is to call #handleExceptionsDuring:context: on your handler class, passing it the block you want wrapped in the exception handler and the current WARequestContext object. You can look at RRRssHandler for an example.

Examples

The Seaside distribution includes a few exception handlers that you can look at for examples of usage. Beware of WADebugErrorHandler and WAWalkbackErrorHandler, though, as they do crazy things to get debugging to work the way we want in Squeak; although they are well commented and may be interesting to look at, they are probably not good examples of general usage. The Seaside-Squeak-Email package includes WAEmailErrorHandler, an abstract class that can be subclassed and used to send an email whenever an error occurs.