Erlang on LLVM? or: Outsource your JIT!
[info]chanson
Has anyone been working on using LLVM to do just-in-time code generation for the Erlang virtual machine?

Depending on the design and structure of the Erlang virtual machine, it doesn't seem like it would be all that tough a project. And it could provide a nice performance boost for those projects that are starting to use Erlang like CouchDB and ejabberd.

For an example of what I'm talking about, there's a project called VMKit that has implemented the Java and .NET virtual machines atop LLVM with reasonable performance. Essentially, if you have a virtual machine, rather than skipping either just-in-time or static code generation entirely, or trying to do it all yourself for some specific platform on which you want to run, take a look at what you can do with LLVM and see if you can leverage its code generation instead.

The SmalltalkAgents foreign-function interface
[info]chanson
I learned object-oriented programming first by reading the introductory materials shipped with Digitalk Smalltalk/V and then by using QKS SmalltalkAgents on my Centris 610 and my PowerBook 520. I think that's had a profound influence on my career and outlook compared to those who learned with C++ or Java, for example, almost as profound as the fact that I originally learned to program using Logo instead of AppleSoft BASIC.

One of the great things about SmalltalkAgents compared to most other similar environments was its great integration with the Macintosh System Software. It accomplished this by creating a great foreign-function interface that let you call arbitrary non-Smalltalk code from within SmalltalkAgents easily, efficiently, and in a way that Just Worked when it came to interacting with the operating system. And the fact that it could do this with the 68000-based Macintosh was a pretty interesting feat, due to the multiplicity of runtime models and calling conventions that wouldn't be unified until the release of the Power Mac.

Here's how it worked: SmalltalkAgents extended normal Smalltalk syntax with an additional construct, the ExternalMethod invocation which was effectively a function call with inline type information wrapped in «double angle brackets». (On a Mac, those are option-\ and option-shift-\ respectively; you could also use << and >> if you wanted.) Not only that, but SmalltalkAgents also supported a concept of structured storage for objects that would allow you to pass around C-style structures and pointers to them very easily.

So an invocation of a foreign function would look like this:
  sayHello
    | result |
    
    result := «printf(('Hello, world!' asCString):Ptr):Int».
    
    [result < 0] ifTrue: [ Exception raise: 'Error calling printf.' ].

    ^self
This is a method, sayHello, that invokes the ExternalMethod listed in the module-global ExternalMethodDictionary under the key symbol #printf. It declares the argument and return value types and uses some of the object coercions built into the SmalltalkAgents frameworks to get values that will be passed to the foreign code properly.

Creating a new ExternalMethod is very easy, too, especially if you can rely on it to follow one of a couple standard calling conventions. SmalltalkAgents defines all of the basic machine-style parameter types you'd expect, and you pass them at the call site rather than when creating an ExternalMethod, so you don't have to do all sorts of complicated header file parsing that many other similar technologies require you to use. Instead, you can just create what amounts to a list of the external methods you want to support and provide some way to hook them to their actual code (whether via a load of a code resource or a pointer in an external library), and then you can Just Use Them in your Smalltalk code.

It worked great, and I think it's something a lot of modern systems could learn from.

Come on, folks...
[info]chanson
I mean, really. Duncan isn't that much of a Scary Programmer.

Even if he does have to take the blame for, you know, Ant. And Tomcat.

After all, he got better!

"Uncle Bob" gets a MacBook Pro
[info]chanson
Robert C. "Uncle Bob" Martin of ObjectMentor bought a new MacBook Pro and appears to be very pleased with it. Uncle Bob is quite well-known in the object-oriented design and agile development & Extreme Programming communities, and it's great to see him join the Macintosh world.

I saw him speak at a C-SPIN meeting a few years back — Testing is about Specification, not Verification (March 2003) — and it was painful watching him fight his Windows laptop while setting up. I suspect between his new MacBook Pro and Keynote he'll be much, much happier. I suspect he'll also find Xcode, Cocoa, and Objective-C very interesting too, especially coming from Java and Microsoft .NET.

Steve Yegge describes what's wrong with Lisp
[info]chanson
Steve Yegge, Lisp is Not an Acceptable Lisp:
You've all read about the Road to Lisp. I was on it for a little over a year. It's a great road, very enlightening, blah blah blah, but what they fail to mention is that Lisp isn't the at the end of it. Lisp is just the last semi-civilized outpost you hit before it turns into a dirt road, one that leads into the godawful swamp most of us spend our programming careers slugging around in. I guarantee you there isn't one single Lisp programmer out there who uses exclusively Lisp. Instead we spend our time hacking around its inadequacies, often in other languages.
Steve does a very good job of articulating a lot of the things I dislike about Lisp, especially Common Lisp. One interesting thing, though, is that a lot (but not all) of the issues he raises are addressed by Dylan.

One of the more interesting things about Dylan in this context is that, despite not adopting a traditional message-based object system like Smalltalk or Objective-C (or their more static cousins C++ and Java), Dylan does push objects all the way down, but in a functional style. It appears to work pretty well, making it easy to define both nouns and verbs in the combinations a developer might need, and even (through its hygienic macro system) allow developers to extend language syntax too.

Happy 10th birthday, WebObjects!
[info]chanson
Ten years ago today, NeXT released WebObjects 1.0: Their new platform for creating object-oriented applications that ran on a server and whose functionality was delivered to client desktops through a web browser. You might have called it an application server.

WebObjects was the first of a category of platforms now worth billions of dollars annually. And it's still one of the most advanced.

If you're running Mac OS X 10.4, WebObjects 5.3 is included with Xcode 2.1 and later. Download Xcode 2.2.1 from the Apple Developer Connection member site — you can do so for free as an Online member — and check it out!

Update: Thanks for the props, Wolf! It's been really cool to see all the great things you've done with WebObjects over the years.

JavaSchools and Boring, Easy Work
[info]chanson
In The Perils of JavaSchools, Joel Spolsky writes:
Therein lies the debate. Years of whinging by lazy CS undergrads like me, combined with complaints from industry about how few CS majors are graduating from American universities, have taken a toll, and in the last decade a large number of otherwise perfectly good schools have gone 100% Java. It's hip, the recruiters who use "grep" to evaluate resumes seem to like it, and, best of all, there's nothing hard enough about Java to really weed out the programmers without the part of the brain that does pointers or recursion, so the drop-out rates are lower, and the computer science departments have more students, and bigger budgets, and all is well.
Of course, those recruiters don't actually use grep. They have some "fancy" Access database that a "consultant" wrote for them that looks for keywords in Word documents.

In the discussion thread following the article, there has been quite a bit of pushback from people claiming that it's OK that most schools are turning out a lot of mediocre developers, because most software development work is easy and boring and well within their capabilities.

I'm with Joel. This isn't a good thing. Looking past his immediate rant — "teaching all Java means students don't learn recursion and pointers" — I think the three principal things from computer science that every developer must understand are abstraction, algorithms, and architecture. Pointers are architecture. Recursion is algorithms. Functional programming, object-oriented programming, the database relational model, even operating systems, are abstraction.

The reason that I don't think it's OK to have mediocre developers working on mediocre projects is that, in large part, those mediocre projects shouldn't exist. They are exactly the sort of projects that the Software Factories people want to turn into primarily an assembly task. Boring, repetitive, easy software is exactly the sort of software that should be wrapped in domain-specific frameworks, implemented using domain-specific languages, and pretty much abstracted away.

While I may disagree with a lot of their methodologies and the specific instantiations of what they're trying to do, I'm totally with them on that. One of the reasons so much software is so bad is that people keep reinventing the wheel, poorly.

Mac OS X Developer Mailing Lists
[info]chanson

I just figured I would take some time out for a public-service announcement.

If you're developing Mac OS X software, you should really be on the appropriate developer mailing lists. Apple hosts a number of mailing lists at lists.apple.com and hosts archives there too. The lists are managed with GNU Mailman and have its convenient subscription and administration interface.

In my opinion, the principal lists for application developers are:

carbon-dev Developing software with the Carbon framework.
cocoa-dev Developing software with the Cocoa framework.
java-dev Developing software for Mac OS X in Java.
webobjects-dev Developing web applications with WebObjects and EOF.
xcode-users Using the Xcode Tools suite for Mac OS X development.

There are also a whole lot of task- and technology-specific lists, including lists for developers implementing AppleScript support in applications, developers working with networking, developers creating device drivers, developers writing multithreaded applications, developers working with Xgrid...

Be sure to look through the list of lists to see what else you can take part in.


Xcode 1.5
[info]chanson
Xcode 1.5 is available to Apple Developer Connection members now. Some of the new features include Java code completion, remote graphical debugging, support for the Subversion source code management system, debugger and editor enhancements, and dead code stripping.

Check it out!

Class Clusters
[info]chanson
[info]moonlessnights has a good post on class clusters in the [info]ood community. Class clusters are one of the unsung great design patterns in Cocoa; they don't just encapsulate instance functionality but class functionality as well, and a lot more naturally and flexibly than the Factory pattern used in a lot of Java and C++ development.

Oooo, XAML and WinFX, what a concept!
[info]chanson
So Windows Longhorn includes a new .NET application framework called WinFX, code-named "Avalon."

Turns out I was overly optimistic in my estimation of what Avalon was. It turns out Avalon is just a system for specifying your application's human interface in an XML-based language, XAML, and even specifying what actions in your code will be invoked by different interface elements. And then, get this: You generate C# code from this that actually creates your interface!

Gosh wow, golly gee! You mean I can use XML to drive a code generator? Ooo! Where do I sign up?

Why the hell does it need to generate code just to create an interface? Why the hell does it need to generate code just to associate a control with an action method? What decade are we living in again? Interface Builder on NEXTSTEP was doing this stuff right in 1988. Why are people acting like Longhorn's getting it marginally less wrong than previous Windows frameworks is such a big deal?

There's even a system like XAML available for Cocoa: GNUstep Renaissance. Renaissance lets you specify your interface in an XML file, too, and has for quite a while. (Nicola Pero first released it on December 26, 2002.) The big difference is that with Renaissance, you just load this "gsmarkup" file at run time and it generates the interface and binds it to your objects right then!

What's more, it includes a layout manager system similar to Java's for automatic interface layout. So you don't even need to specify sizes of controls, you can just let the layout manager handle everything.

Here's what a simple gsmarkup looks like (taken from the Renaissance Tutorial):
<gsmarkup>
  <objects>
    <window title="This is a test window" closable="no">
      <button title="Print Hello!" action="printHello:" target="#NSOwner" />
    </window>
  </objects>
</gsmarkup>
This gsmarkup file specifies an NSWindow with no close box and the title "This is a test window", which contains an NSButton titled "Print Hello!" that sends -printHello: to the object that owns gsmarkup (set when the gsmarkup is loaded). Note that you don't have to specify the size of the button, where in the window it is, etc. You can if you want (or need) to, you just don't have to. And if you don't specify a target for a control, its target is assumed to be nil (the First Responder).

You can use id attributes in a markup to name objects and then refer to them using the standard reference (#foo) notation to wire them together within the same gsmarkup file.

And here's something even cooler: When a gsmarkup is loaded, an associated strings file is also loaded if one exists. This makes it very easy to translate gsmarkup-based applications into other languages. You don't even need to change the gsmarkup (especially if you're using layout managers) — you just have to supply a strings files for the other languages!

And possibly the best part: Renaissance works on both GNUstep and Mac OS X. You only need to maintain a different menu structure between the two; for everything else, you can use a single set of markup files (assuming you're using layout managers). One codebase, one interface spec, multiple platforms.

For more information, check out the Renaissance Manual.

Microsoft PDC Prediction: Avalon
[info]chanson
Lots of people in the weblog universe are talking about the upcoming Microsoft Professional Developers Conference in Los Angeles. The PDC is where Microsoft is going to be shipping their first development release of Longhorn, their successor to Windows XP.

There are a whole lot of code names being used for various different parts of Longhorn. Aero, Avalon, etc. Just keeping track of the code names is a full-time job.

One of these, Avalon, corresponds to some new application framework that people like Scoble keep hinting will make the browser obsolete. They say it'll do this by turning the operating system into some sort of fancy information environment. Here's my prediction for what Avalon really is...

I think Avalon will do the following: First, it will let developers specify their application's interface via XML, probably through a graphical interface editing utility. Second, it will let developers bind model objects directly to interface elements. Third, it will let these model objects reside — somewhat transparently — on a server, rather than on the client side. Fourth, it will exist as a runtime that is included in Longhorn, so an actual Avalon application will consist of an assembly containing only the XML interface files and the code for the model objects or model object proxies.

In short, Avalon will be an implementation of something Apple has had for several years now: WebObjects Java Client. With Java Client, you build your interface in Interface Builder, which generates an XML file. You use the EOInterface framework to bind interface elements directly to model objects and operations on them (queries etc.). The model objects live on a WebObjects application server, but they can have custom client-side proxies that you can add your own logic to. The Java Client runtime is a jar file that can either be downloaded to the user's machine as necessary via Java Web Start or as a Java applet, or it can be installed directly on a client machine.

There are two twists to this: Java Client works pretty much anywhere you have a Java 2 Standard Edition 1.3.1 or later virtual machine. That means you can deploy Java Client applications to Mac OS X, to Windows, and probably even to Linux and Solaris and all sorts of other platforms. More importantly, though, Java Client is itself just a foundation for Direct to Java Client.

Direct to Java Client automatically generates Java Client applications on the fly from a data model and a collection of business rules. You can customize the objects used in the data model and you can customize the business rule set to customize your application; you don't need to spend your days in Interface Builder laying out interfaces for CRUD (create, read, update, delete) applications.

Avalon sounds like great news for Windows desktop application developers. After all, in 2006 they'll be able to get benefits that have been available since the late 1990s with WebObjects!

Objective-C Language Enhancements
[info]chanson
There have been some Objective-C language enhancements in GCC 3.3 that are available to anyone working on Mac OS X Panther (10.3). Unfortunately they require support from the Objective-C runtime, so while they integrate correctly with code developed on earlier operating systems, code that uses them won't run on earlier operating systems. Hopefully these changes will also be supported soon by the GNU Objective-C runtime used by GNUstep; they seem very useful.

There are two major enhancements described in the GCC 3.3 release notes included with the August 2003 GCC update: A Java-style exception handling system, and Java-style synchronization blocks. Previous versions of Cocoa used preprocessor macros based on setjmp and longjmp for exception handling, and neither the language nor the framework had built-in thread synchronization primitives. (Cocoa did have several classes providing synchronization functionality though.)

The new exception handling system is modeled very closely on Java's. In the current system, you write code like this to handle exceptions:

  NS_DURING
  {
    ...
    [NSException raise:NSInvalidArgumentException
                format:@"Invalid argument '%@' to method 'blah'", argument];
    ...
  }
  NS_HANDLER
  {
    if ([[localException name] isEqualToString:NSInvalidArgumentException]) {
      // handle the exception
    } else {
      [localException raise]; // re-raise the exception
    }
  }
  NS_ENDHANDLER

Now, you would write the same thing as:

  @try {
    ...
    @throw [[[MyInvalidArgumentException alloc] init] autorelease];
    ...
  }
  
  @catch (MyInvalidArgumentException *invalidArgumentException) {
    // handle the exception
    
    @throw; // re-throw the exception if it wasn't handled
  }
  
  @catch (id allOtherExceptions) {
    // all other exceptions will be caught by this general construct,
    // or if it isn't used, they'll be caught higher up the call chain
    // just like in Java or C++
  }
  
  @finally {
    // do any cleanup that needs to be done regardless of whether
    // an exception was thrown
  }

This will lead to much cleaner code in the long run, and it's a much closer match to what many developers new to Cocoa and Objective-C are used to as well. Everybody wins!

The other feature added is Java-style serialization. It's not a complete implementation of Java's synchronized keyword; the only thing supported are blocks synchronized on a single object. But that's plenty because it means you no longer have to maintain your own NSLock for simple synchronization:

  NSMutableArray *sharedQueue = [[NSMutableArray alloc] initWithCapacity:5];
  
  ...
  
  @synchronized (sharedQueue) {
    // add an element to the shared queue
    [sharedQueue addObject:object];
  }
  
  ...
  
  @synchronized (sharedQueue) {
    // remove an element from the shared queue
    if ([sharedQueue count] > 0) {
      element = [sharedQueue removeObjectAtIndex:0];
    }
  }
See? Much more straightforward than managing a separate NSLock. (Of course, for shared work queues you're generally better off using an NSConditionLock but this is just an illustration...)

Hopefully Objective-C will continue to evolve and do so in the right direction. There are some interesting discussions taking place now about the language's future in various places like the GNUstep discussion list and the comp.lang.objective-c Usenet newsgroup.

Sneak Preview: Using JUnit with Cocoa-Java
[info]chanson
There's going to be a little write-up later describing how to ues JUnit to do unit testing of Cocoa-Java applications. I wrote an overview of how to do it on the Apple Cocoa-Dev mailing list; I'm going to polish that up and post it here. And maybe on my company web site as well — it's been a while since I posted an article.

BDXmlRpcForWO
[info]chanson
Early this morning, I shipped BDXmlRpcForWO 1.0.0, an Open Source framework that lets any WebObjects application vend XML-RPC web services with just a couple lines of code in your application's constructor.

All you need to do is register the XML-RPC request handler, and then register a class whose public methods you want to expose via XML-RPC. In other words, it works almost exactly like the SOAP support in WebObjects 5.2.

BDXmlRpcForWO requires WebObjects 5.1 or 5.2 (though it may work on earlier versions too, I haven't tried) and Apache XML-RPC 1.1.

I wrote it in a couple hours after skimming an article in the January, 2003 issue of Linux Magazine by Rogers Cadenhead. It described how to use Apache XML-RPC. I thought, "Hey this would be easy to wire up to WebObjects!" And it was!

Oh, and AppleScript's web services support is pretty cool, too. It let me test my request handler really easily.

It's alive! It's alive!
[info]chanson
I now have a PC almost ready to put into colocation.

The PC itself is an AMD Athlon XP 1500+ machine I bought from Century Computers. It has a KM266 chipset, 1GB of PC2100 DDR RAM (which I bought from MemoryX and installed myself), and two 40GB disks (7200 RPM). It's a pretty unremarkable machine, really, but it works and it was cheap.

It's running Red Hat Linux 7.3 Professional, FrontBase 3.6a, and WebObjects 5.1.3.

Getting Red Hat Linux set up was a royal pain in the ass. Red Hat's default disk partitioning is utterly screwed up; it didn't give me enough space in /var to actually update all of the packages installed as part of the operating system with up2date! How stupid is that? So I wiped and reinstalled, and had to fight the interface to the stupid disk partitioning tool in order to get it set up how I wanted it. How did I want it set up? On drive 1, I wanted the usual 48MB (or whatever) /boot partition, followed by swap space, followed by a single huge / partition. And that's only because it complained at me about not having a separate /boot partition; I would have rather had just a single partition per disk. Drive 2 is a single partition that's going to serve as backup for the time being.

Also, convincing the machine to start Apache at startup was stupidly difficult. You'd think that since I told Red Hat I was going to use this machine as a web server that it would start it automatically, but that would be too easy. I had to use /sbin/chkconfig --add httpd to add httpd (not apache as one might think) to the startup process. But wait! That didn't add it to the startup process! What did it do? Evidently, it just made it visible to the startup process. To actually add it to the startup process, I had to use /usr/bin/ntsysv -- whatever the hell that stands for -- to go through a little list and put a check next to httpd. Then, finally, it would run at startup.

All in all, the Red Hat installation and administration experience was significantly worse than that of Windows. And I'm a Mac OS X guy, so from my perspective it really sucked!

Getting FrontBase, Java, and WebObjects set up was far, far easier. FrontBase and Java were both utterly trivial, since they were provided as RPMs. (I'm still amazed that the FrontBase RPM is only 3.2MB in size, since FrontBase is such a competitive RDBMS.) WebObjects was a little more difficult to set up, but that's only because Linux isn't a supported deployment platform.

Google pointed me to HOW-TO: Setup WebObjects on Linux by Stefan somebody at TET Labors in Germany. (He doesn't sign it, so I'm just guessing from his email address.) It's based on the journal Jon Rentzsch kept last year when he was putting together a WebObjects deployment server running Linux. Fortunately, life is considerably easier now.

Here's what I did differently than in the HOW-TO: First, I didn't install my own version of Apache. I just used the version of Apache that came preinstalled with Red Hat, so it'll be automatically updated by up2date and because I can't imagine how much my life would suck if I tried to make my own version of Apache interact with the Red Hat startup system. I also installed the Java2 version 1.3.1 (update 04) SDK instead of 1.4, because WebObjects is really only qualified against 1.3.1 right now. (Maybe when WebObjects 5.2 ships it'll be qualified against 1.4, and I can upgrade.) I also had to adjust the instructions to refer to the proper paths:
  • Apache's configuration files are in /etc/httpd/conf
  • apxs is at /usr/sbin/apxs
  • Apache's header files are in /usr/include/apache
  • Apache's default cgi-bin is at /var/www/cgi-bin
  • Apache's default document root is at /var/www/html

I think that was it.

All in all, it didn't take me nearly as much time to get it working as I thought it would, and it seems to be working just fine. There's only a couple more things I want to get installed and configured before I take it to its new home on the South Side: Webmin, Mailman, and some additional security stuff.

Cocoa Browser
[info]chanson
In case I haven't recommended it, anyone doing Cocoa development will want a little application called Cocoa Browser by Hoshi Takanori and Max Horn. It's a spiffy documentation browser for Cocoa. It makes reading the Cocoa overview and reference documentation much easier - even without indexing.

Cocoa Browser can show both Objective-C and Java documentation for Cocoa classes, methods, etc. It scrapes the docs that are installed with the Mac OS X developer tools, so it's never out of sync with what's on disk. And it provides a nice column-browser interface: Framework classes & protocols in one pane, classes in the next, topics & method types in the next, and finally the methods themselves.

It's also released under the GNU General Public License, and the 120KB disk image Cocoa Browser ships on includes the complete source code. It's reasonably clean & straightforward to figure out what's going on inside it by glancing through the code and setting a few debugging breakpoints.

Hoshi also has some Mac OS X Programming Tips on his web site related to the development of Cocoa Browser.

Tools, Refactoring, Java
[info]chanson
I'm an unabashed Objective-C fan. I think it's one of the best languages around: The Smalltalk object model with complete access to C. On the other hand, the fact that it's C makes it harder to develop high-end Objective-C tools.

I'm reading Refactoring by Martin Fowler right now, and it's totally obvious how not having a preprocessor or header files can really help in creating tools like a refactoring browser. (Of course, I learned object-oriented programming with Smalltalk, so I know firsthand how useful tools like that can be.)

I want some of this stuff for Objective-C but unfortunately it's going to be a whole bunch more work for the tool vendors to ship. Not as much work as it would be for C++, but definitely more than it is for Java. (Maybe someone needs to create a new language, like Objective-C but without the C baggage like header files, machine pointers, or a preprocessor. Sort of like WebScript with classes. It could be called "Objective" or maybe "Smalltalk"...)

DirectToWeb fun
[info]chanson
I've been looking at DirectToWeb a bunch more. DirectToWeb is a system in WebObjects that dynamically generates web applications on the fly based on rules that are evaluated and fired in a context. It's leveraged by DirectToJavaClient to generate Java applications on the fly in exactly the same way it generates web applications on the fly. Trust me, it's really, really impressive. Everyone I've demoed it for has been blown away.

For instance, when you invoke an "edit" action for a particular entity (by clicking an "edit" hyperlink, for example) it sets the "task" key in its context to "edit" and the "entity" key to the name of the entity being edited. Then it determines what type of page to render, and while it's rendering the page it will resolve any number of other requirements by going through the rule system and firing rules that are specific to what it's looking for and its current context.

Another, more detailed example: To determine what property keys a particular entity has, DirectToWeb asks the rule system for the "propertyKeys" value with the entity, task, etc. set appropriately. You can create a rule like
(entity = 'MyEntity') => propertyKeys = "(name, creator)"

to return an array indicating that "name" and "creator" are the properties in the MyEntity entity. Say, though, that MyEntity objects should only show their name property in lists. You can add a rule like
((task = list) and (entity = 'MyEntity')) => propertyKeys = "(name)"

and the most specific rule will be fired. And the best part is that all this is extremely flexible: Rules don't have to be created in your Java code; you can specify rules in a data file as well. Rules don't have to perform simple assignments; they can perform more complex assignments using Key-Value Coding, or even use custom Assignment subclasses. And so on. The rule system itself can even be subclassed and extended, though the WebObjects team hasn't yet added delegate support to it.

There's even a nice graphical rule editor that uses the standard qualifier editor control for building the left hand side of a rule and lets you set Assignment subclasses, keys, and values for the right hand side.

It'd be nice if the WebObjects team would do just a little more refactoring, and make the rule system slightly less coupled to DirectToWeb than it is now. There is some indication they're going to be doing this; they have "this implementation is subject to change" stamped all over the documentation. And there's no delegate support yet, unlike everywhere else in EOF and WebObjects.But it's not like Apple would license it separately from WebObjects anyway...

Travel Annoyance
[info]chanson
I should go to Apple's World-Wide Developer Conference again this year. I want to. I'm just trying to work out all the costs.

Grr. Why is everything so damn expensive? It's not the conference itself that concerns me, it's the hotel price.

Oh, and a boot to the head to Apple for hiring some company to run a consolidated hotel reservation web site (with extra-special conference room rates) that I can't use on Mac OS X. It has some stupid Java applet so they can show little pushpins where hotels that match search criteria are. It doesn't work in either OmniWeb 4.1sp57 or Internet Explorer 5.1.3. I bet if I wrote the company, I'd get email back saying crap like "Do you have the latest Java plug-in from Sun? What version of Windows does MAC include anyway?"