Keep Working, Worker Bee!


Well, the contest web page is finally up. This is one of a few different contest-related projects I've been working on, so I'm glad I can finally start talking about it. Check it out: there's a mailing list you might want to subscribe to and a bootable linux CD you might want to download if you're interested in competing when the contest starts at the end of the month. We've been Slashdotted about it, which is neat. I don't think I've ever been Slashdotted before.

Other than that, meetings and writing and stuff like that. Oh, I officially heard back about the ICFP paper; we got rejected. We'll submit it to the Scheme Workshop instead, and it's probably a better fit there anyway.


Messed around with bootable Linux CDs. Prepared web pages for release. Messed around with BitTorrent. Basically, today I played IT worker except I also did a lot of game design. At this point "design" isn't quite the right word, it's all just about tweaking the wordings of things and arguing the finer points of various corner cases and so on. It's more like development, and even more like polish.

It's amazing how very slowly things seem to go when it actually matters that you not screw up.


More secret stuff, punctuated by a couple speeddating crises in which the psychologists thought a particular important question that was first presented today was saving incorrectly, but it turns out that the first three people who answered it were just weirdos who didn't want to fill out that question for whatever reason.

I promise these things will get more interesting once the programming contest starts and I can talk about it.


There's really not much I can say about Wednesday. The programming competition is consuming an increasing portion of my work time, but obviously I can't tell you what I'm doing. I think Thursday or Friday there may be some news to announce, but I really don't even know whether that'll come to pass or not. So I guess the only thing I'll say is that I spent about 30 minutes and added a feature to the speed-daters web site after realizing that the deadline by which it needed to be in live use was, um, tomorrow. It was no big deal, but still it was annoying.

I'm growing less and less enamored of Scheme's untypedness. Even contracts can't help you in some situations, because values don't have principal types: I was in a situation where I wanted to extract an element from a list using Scheme's assoc function. assoc, if it had a type, would have type α * (listof (cons α β)) -> (union (cons α β) #f). It takes a value and a list of pairs and returns the pair in that list whose first element is the given value, or #f if there is none. You'd imagine that you could write a contract that would enforce the same behavior, but you can't: if someone gives you a string and a list of pairs whose first elements are all symbols, how do you know the set the programmer was thinking of wasn't the union of the set of strings and the set of symbols? Or that particular string and those particular symbols? Or maybe the set of all those particular symbols except one, in which case the contract should alert you that it's been violated? The solution is just not to use this kind of polymorphism in contracts, and if you know more about a particular call-site then make a specialized function with the contract there. But that's obviously a pain.

I think I may be coming around to the opinion that my ideal language would have a static type system with an explicit mechanism for allowing you to turn it off, or allow you to run code even if there's a type error somewhere. Basically the way you used to be able to do with MrSpidey in DrScheme; write some code, typecheck, fix every error it finds or come up with a justification for why it's not actually an error, repeat.


Three things.

  • I worked on secret stuff for most of the day. At least some of the secret stuff I've been working on ought to be unveilable very soon; that'll be a big weight off my shoulders. The ICFP contest initial start time gives us four and a half weeks to sort out everything. That'll be a relief.

  • I went to see a talk today by Derek about recursive abstract datatypes. It was interesting. I talked with Dave about the multilanguage stuff; he's interested in the project, so I'll probably give him a more thorough talk about it next week. Hopefully he can give me a little more insight into the interesting type system issues I could address — he already gave me two without even thinking about it, actually.

  • I looked into doing pivoting on a PostgreSQL database as a part of the speed dating project. I had hoped it could be done in-engine, like you can do with Access databases; it turns out that's not really true but you can pretty easily write scripts to do it if need be. That wouldn't really work too well if I needed to have data flowing into the database at the same time we were analyzing it, but I don't so this'll work. I understand that normally pivoting isn't necessary, but in my case I don't really see a way around it, so I really do wonder whether my situation is one that just can't be handled with standard database stuff.


I am having the hardest time finishing off this one system for the multilanguage paper. Every time I think I've finally got it done, I realize that I should've arranged something differently, or I need another nontrivial lemma to establish that something holds, or my induction doesn't really work, or whatever. And by the time I'm done, the goal is to make it look obvious enough that people's eyes will glide right over it without even having to pause. I'm beginning to fully appreciate Pierce's quote about how the proofs are almost always boring if the definitions are correct, but the definitions are almost always incorrect.


In the morning, much work was done on the multilanguage system I've been working on, and it's now just about done. Which is good, I've got a lot more I want to do with the framework I'm developing, so it's good to be making progress. After that, some work was done on the paper response; we worked with renewed vigor today after learning there is a slim chance the paper will still get in, and also because of our sense that some of the reviewers' misconceptions can be cleared up with just a few words of explanation. Things are still looking pretty bad for the forces of rad, but we've got at least one person weakly in our corner and another person who we might be able to nudge over into friendly territory.

Finally, some work was done on the contest. You'll have to wait for that one.


That certainly sucked.

So today — I'm sounding like a broken record — I mostly worked on secret stuff regarding the contest and doing a proof of something. I actually made quite a bit of progress, but the report would be pretty boring. Wait for my paper, it'll be good.

Anyway, the most important thing was that at 5 I got preliminary, we-get-48-hours-to-respond-type reviews for the paper Robby and I submitted back in April. It did not go well. The reviews were, um, mixed, including some good stuff and some stuff that seemed almost deliberately insulting (I shouldn't get into more detail here). But overall, the reviewers weren't very impressed. Damn. I could say that I had never thought the paper was a good fit for the conference, and I have documentation proving that's true, but it still sucks to get bad reviews.


Big two-day update!

Though I haven't been working like a fiend, I have found myself for two days in a row working from the early hours into the late; that has cut into my Worker Bee! updating time, alas.

On Tuesday, I spent the morning installing Gentoo for a particular purpose. Gentoo I feel like is more than a little absurd as a concept, but the installation guide at least is pretty thorough and if you're willing to think about what you're doing isn't too bad to get working. The afternoon was all spent doing secret stuff for the programming contest, which was fun and is progressing nicely.

After work, I had to get home quickly because I had a meeting with the psychologists about the speed-dating project. It occurs to me that I may never have explained what the speed-dating project is, so I suppose I ought to do that before I continue: during my first year as a graduate student, I had a friend doing a PhD in psychology at Northwestern, which is also in Chicago. There was at the time a new faculty member there who had planned a big year-long longitudinal study that had an essential web component, and he found that due to a particular methodological twist he wanted it was impossible to find anyone who could write the web part for him. Said new faculty member was banging his head against a wall and about to give up on the whole project when he happened to ask my friend whether she knew anybody who wanted to do psychology programming on the web for cheap. She knew that this is an issue I've been interested in ever since undergrad, so she gave him my name, he called me, I roped in a friend of mine and long story short we made him a web site. We even wrote a little paper about it afterwards.

Now we go to February of this year. Psychologist wants to do another online study, and since the first one went so well with me he writes me and asks me again. This one is about speed dating; he wants to study people's initial attraction to each other and he thinks a speed-dating type event is a great way to get people interested in participating and generating the reams and reams of data he'd want to collect from them. Speed dating generally has web site support and his study will definitely need a lot of it, so he asks me. I'm intrigued, I'm interested in exercising my web programming muscles again, and so I say what the heck.

The speed dating site turned out to be intensely more complicated than the first study was, and I was doing this one myself so it was a pretty major task. But I ended up making it work, and 160 little speeddaters used my software and 7 speed-dating events went off with -- well, not without a hitch, but with few enough hitches that it certainly seems like a success rather than a failure. The study's not done yet, but all that's left now is the speeddaters come to the website every 3 days and answer some tracking questions; they'll do that for the next couple weeks and then the whole thing will be over.

So Tuesday night I met with the psychologist and his grad student (who actually was the leader of this particular research project). I did this for free in exchange for a co-authorship on the paper, but I think they feel like I got a raw deal (I don't think that myself, incidentally) and they ended up buying a Cuisinart fancy food processor and a digital cooking scale as a thank-you present. Woot! Thanks guys!

Then we spent the evening talking about two things: firstly, they are somewhat interested in turning the speed-dating thing into a product we could sell. Second, the data had to be stored into a relational database this time, and they understand why that's good but have no clue how to write queries sophisticated enough to answer their questions. I'll need to either teach them or do the queries myself. Maybe there's some magic report-writer software out there that'll let them do all the queries they want without having to know any appreciable sql? There are lots of fancy things necessary for this data, though; things like self-joins and and left joins and aggregation and sql builtin functions and so on. The meeting broke up around 10PM.

So that's Tuesday. Today was spent basically worrying about my ICFP submission, the reviews for which I will get to see on Friday but had hoped maybe would magically get sent out two days early (they weren't). I also spent a lot of time working on doing a more proper job typesetting some of the multilanguage stuff and doing some proof stuff; I'm going really slowly on it, I fear, because the straight-ahead inductive proof misses the elegance of the model and thus I fear misses an important opportunity to avoid re-establishing things we already know to be true. (This is difficult to talk about without explaining everything; if you see me in person and you care, ask me and I'll explain it further.)

I did discover several things; among the most interesting was whizzytex, an incremental LaTeX viewer as an Emacs minor mode. From those ever-impressive French hackers at INRIA. They point out that it works best with Active-DVI, an allegedly-superior xdvi replacement also out of INRIA and written entirely in OCaml. I really need to learn OCaml one of these days. I've heard it called "the Common Lisp of ML" where that phrase was intended as a pejorative, but as I program more and more I think I've decided there's an important place for the world's Common Lisps.

Then tonight I called up my friend who worked with me on the original psychology study, who now lives in Boston and with whom I hadn't talked in at least a couple months or so. I told him about what I'm doing, and he had yet another possible application for the multilanguage research I'm doing — I tell you, everywhere I look there's somebody else with some problem I think my system could solve, I'm really thrilled about it. Then we talked for a very long time about making a viable, potentially commercial version of the system we've been talking about since the original psychology study. He's interested in going for it, especially if there's actual money involved. That's great; I'm not sure I'll be able to join the business venture myself, at least not until I get my PhD, but since everyone else seems willing and able to start on it right away I think it'd be foolish for them to wait on me. I really hope we can get something good running, though, I think the social science world can really use this sort of thing.

Things are looking pretty exciting on that front. Right now, though, the most important thing is for me to stay on target research-wise, so that's what I'm going to do.


So last night I submitted a paper review. This morning was dedicated to patching that review after I realized there was a small but significant bug. Thanks to the journal's editor for being understanding and holding off on mailing out the review until I was able to fix it up.

After that there was a little working on the semantics stuff. I finally got organized enough to make a list of the results I've proven and the directions I want to pursue. I also got started on an important proof, though I didn't really get anywhere much, because I actually spent quite a bit of time working with our contest beta-testers handling issues in person and over the mailing lists. I ended up spending entirely too long writing a program to perform what ought to be a trivial Unix task; I wish I could say what it was but I'm afraid if I told you what it was you'd figure out the whole darned contest and that would be totally unfair. We certainly wouldn't want that.

But all that got interrupted when my Amazon shipment came in. Barendregt, Essentials of Programming Languages, and Lisp in Small Pieces. All classics, and I've read none of them. It turns out that none of them are really for skimming either, so I started in on Barendregt at school and brought EoPL home with me. EoPL is certainly less dense than Barendregt, but I'm worried that if I were to teach it I'd end up assigning way too much to my students because everything I've read so far seems like it couldn't possibly take any time. Honestly, I don't in my gut think it'd be unreasonable to assign all of chapter one including every problem in between, say, a Thursday and a Tuesday class session, though I know intellectually that's not reasonable for the kids. But it does seem great.

Question: EoPL seems like it'd be a good intro book focusing on interpreters and dynamic semantics; Pierce's Types and Programming Languages is great for teaching type systems. Are there comparable books for teaching compilers in a functional context? I know of Appel's Modern Compiler Implementation in ML, but I'm not wild about it (and it doesn't even really talk about implementing functional features until the end anyway). Anything else out there?


Good progress today on a couple fronts. First: I spent some time trying to pin down the problem I was talking about yesterday, and realized that the real problem was that I was being naive in the way I distributed guards across functions. If you track who's asking, i.e. whether you're guarding an ML value from Scheme or a Scheme value from ML, everything works out. It's actually just like how blame-tracking works with contracts. Very cool.

I also sent the task for the contest to the beta testers. The fun begins now as people's problems are starting to roll in (it's weird, for instance, how few people know what an HP48G is). After that we had the contest meeting, which was actually unusually productive. And it seems to have spawned a big email discussion, which is good.

Now the weekend, which will probably be at least partially for work.


Lots of little things today. I actually didn't end up doing that much, but I got little pieces of lots of things to happen.

The morning was essentially for the programming contest spec. Another draft out the door, more revisions, et cetera. I also spent a good chunk of the afternoon doing the same thing. It's surprising in a technical specification how difficult it is to make everything work out, how many tiny things there are that have to be right or else the document doesn't work. Makes it a big pain to get these things right.

Also today the guy who maintains the PLT Scheme web pages did a new revision of the code that generates HTML. Since I control the PLaneT page, that means I need to propagate the updates there.

After I found out about that, I started up on the declarative images and 3D anaglyphs paper I was talking about yesterday. After about a paragraph and a half, I ran into Robby and he said he thought it'd be better to scrap the whole thing to focus more on the foreign interface semantics. I'm kind of glad of that, actually. I tend to sign myself up for too much, and while this wasn't a lot it isn't like I have nothing to do.

So when that was done I worked on the multilanguage semantics stuff. Robby thought that the right way to represent the dynamic checks you need to guarantee type soundness was as guard functions that get distributed just like contracts. I worked that out, and actually I'm not sure that works well -- the thing is, exactly what needs to be checked, if anything, depends on the reason the conversion is being performed, and that information isn't known if you separate the dynamic checking out from the conversion process ... I think. On the other hand, having the dynamic check separate from the translation is useful to a major theorem I want to prove, so I did a version where the mapping functions introduce guards but those guards are represented separately. The guards don't distribute, though: they're just straight up projections. Probably you could show that guard composed with translate composed with translate back is a retract or something, I dunno, sounds plausible. We'll see.

In other news, I'm a big nerd. I got my tax refund and decided to spend it on a few expensive computer books I've wanted for a long time. So now, coming to my office courtesy of Amazon: The Lambda Calculus, one of the few books important enough to be known by the last name of its author, Essentials of Programming Languages, and Lisp in Small Pieces. I've wanted all of these books for a long time, so I'm glad to be getting them. Thanks Uncle Sam!


In the morning I fixed a minor speed-dating crisis, but otherwise today was spent almost entirely on super-secret stuff. I released a draft of the spec to the committee by 11:30 and then went to the thesis defense of the guy who works in the office next to me. When I got back I had plenty of feedback, and I spent the rest of the day incorporating it. Afterwards there was another release, and then I went home.

Sorry, not much more can be said about that.

I'm trying to make up my mind about whether or not I absolutely hate LaTeX or just mostly hate LaTeX. It's the epitome of the whole Unix aesthetic: everything is doable, but nothing is easy. I frequently get upset about things like changing margins or dealing with stupid figure problems, but on the other hand you can define macros and at least somehow get the abstraction you want. The rules for what works where and what affects what seem byzantine to me, though, so my abstractions are a lot less abstract than I'd like, but I'm not sure whether that's me or it. Probably both.


Mostly a collapsing day, which is not my favorite kind. In the morning I tried to update my reduction systems for the multilanguage semantics work I'm doing so they were arranged in a new way that facilitates a really nifty connection between this work and Robby's contracts. COLLAPSE! Turns out that broke everything; while it's still possible to do it, it's going to be much more involved than it was supposed to be. Then I worked on the programming contest some; and as I checked the old tasks for some inspiration I noticed something odd: our task was by far the largest that had ever been published. COLLAPSE! I went into a revising frenzy trying to reduce it, and when I did, DOUBLE-COLLAPSE! I accidentally dropped some important stuff and I'm going to have to make sure I've fixed all that today.

After that was the contest organizational meeting, and then for some reason that now escapes me I signed up to write a small paper for [FDPE '05] about the [3D lab] I designed last year and the teachpack that makes it work. Only 5 pages, due in 3 weeks. But unfortunately it seems like the paper may focus much more on the teachpack, which I didn't write, than the lab, which I did. Oh well. The conference is colocated with ICFP, to which I'm going anyway, so it seems like submitting something is in order.

Then I biked on home and after literally collapsing and then recovering, I checked my email. COLLAPSE! Something mysterious was wrong with the PLaneT code; new versions hung when they were supposed to be downloading from the server. COLLAPSE #2! There was a huge problem with the speed-dating site: on the followup, on the _one_ page where I wrote the questions directly I'd missed a couple. As it happens, those questions were apparently critical to the entire endeavor so I had to do some speed-coding to fix it.

Reasonably hectic. But the weather was gorgeous.


Just a few things today.

  1. I finally set up automatic nightly backups for the speeddating site. Cron is really a cool tool, I have to say, though I also have to say the interface kinda blows. It's really magical the first time you get an email from cron telling you about successful completion of some job you'd forgotten you scheduled. Pretty neat.

  2. After that I dug up a bunch of the multi-language semantics stuff, brought it up-to-date with the new version of Redex, and got a simply-typed lambda-calculus embedded in an untyped-lambda calculus system working. That system, as it turns out, is pretty cool: you actually end up getting higher-order contracts in the Findler and Felleisen sense for free out of the embedding. After I'm done demonstrating that result a bit more formally, I'm going to show that embedding simply-typed lambda calculus onto itself can actually give you a language that's not strictly normalizing. Then there's a couple proofs, and then the big task of modelling and then proving correct a large system. This research is turning awesome everywhere I'm looking. Yay.

  3. I read up on Reynold's separation logic for the PL reading group meeting. Nobody really understood the paper very well, but it seems very interesting nonetheless. We're reading about it again next time.

  4. And some other stuff not really to be shared.


Unlike the rest of the days this past week, today I pretty much did one thing all day: worked on the ICFP contest. It's dawning on me now that as the ICFP contest is sort of a secret, its ascending importance to my average workday may have a serious negative impact on my Worker Bee! reporting. So I'll tell you all the banal details, but not anything significant. To whit: I spent most of the morning munging HTML files into LaTeX files, as I did yesterday. After that was done, I spent some more time copyediting and bringing my horribly deficient TeX skills up to speed. After that there were some substantive content changes, followed by fast 'n' furious office-equipment usage section and a meeting at 3:00.

The point of all this is that we're going to be having a dry run of the contest in the next week, and at this point we need to be ready for it. So we work.

Hm. I don't think I can tell you much more than that.

On the Speed-Dating front: multiple transgressions per person need to be implemented. That is the absolute last thing. In 40 minutes the last kids will roll over into followup mode, and at that point we'll be completely done with 2/3rds of the code I wrote for this project. Weird.


It appears as though people are paying attention to the speed dating study, much more than they did to the freshman dating study of a couple years ago. That article is actually the second that ran in the Daily Northwestern about our little project; the first was some guy's column. It was funnier, though this one was more informative about what the project is actually about. Reading these articles it becomes clear exactly how weird my own perspective on the project is: as far as I'm concerned, the actual speedily-dating-people part of the project is pretty much incidental, just a way of knowing when to roll events over into new phases.

Actually, as of last night all the speed dating is over, and as of tonight every match has been announced. Tomorrow at 5 the last group of daters will get rolled into the followup, and then the project will be all over but the data collectin'. The problem with real data collection is mostly that people want to do weird things that totally screw up your nice models; no you can't fill out the intake survey after you do your matches because I didn't think of that and it doesn't make any sense anyway. Computers don't allow you to bend the rules and neither do I. Hrmph. (We've had to deal with a couple rule-benders. It's been annoying.)

On an unrelated note: how cool is this? In SLIME, as in DrScheme, esc-p in the REPL pulls up the previous expression you evaluated. BUT: in SLIME, if you've already typed something at the REPL (without evaluating it), you get taken to the previous expression THAT STARTED THAT WAY. Awesome! It wins one point. I looked into adding that to DrScheme, but couldn't figure out how to do it in 30 minutes or less so I gave up. But it'd still be cool.

I then spent the next many hours trying to convert our programming contest documentation from HTML, which for some reason I thought would be a good first draft format, to LaTeX. Didn't finish. Without being too specific, there are lots of diagrams, and it turns out to be a big pain to get everything from a PRE tag looking okay when typeset. I developed some perl and Scheme helper programs and found somebody's precooked binary to do the conversion, but it was still lots of work. Moral of the story: for all its faults, LaTeX has enough meta-information in it to make it a good document format for converting to others. HTML has no such property. So make your documents be LaTeX by default.

That's pretty much the size of what I did today, other than talking for a while about the status of the multilanguage semantics work (we've solidified a direction) and managing to totally embarassingly screw up deployment of the feature I wrote for the speedy daters yesterday (forgot to copy a file, server yelled when people tried to fill out their survey).

After I got home, I installed OS X Tiger and started playing with Dashboard widgets. I'm totally going to make a MzScheme or MrEd REPL widget. Totally.


"If you define a program, then highlight it, then right click the mouse one of the options is collapse SEXpression. You have no idea how funny 9th graders think this is."

That's a bug report from this afternoon, just before I went home. The SEXpressions were a-flyin' today, too, metaphorically if not literally. For one thing, Robby got back from Taiwan after a two-and-a-half-week trip. The practical upshot of which was not too much, I wasn't waiting on him for anything, but it does mean I ought to get started prepping my class for the summer quarter.

Robby came by in the morning. After we talked, I had a little work to do on the speed dating site: seems people are having "no relationship" with far too many people, the result of which was that people were answering like two questions per followup and being on their merry way. We have a solution, which was to ask people to give the names of other people they may be seeing until they've filled up four slots (or they run out of relationships total). Lucky for me, I'd already written most of the code before. So this new feature was a quickie. It gets launched tonight pending a few more changes the psychologists told me about over the phone a little while back.

There was a dissertation defense at 3 I wanted to see, so I had an abbreviated workday. I only did one other major thing, which was reading over a paper I'm reviewing. I've read the paper over before, but it's both long and difficult and even after reading it over again I don't really understand it. That blew a few hours, though.

I did a bunch of little stuff too, nothing major. Added a few packages to PLaneT, though Jay tells me he's writing a package-upload client so I won't have to do this much longer. A little chatter on one of my mailing lists inspired me to share the read-all-sexps trick Robby once showed me: to read every S-expression out of a given port as a list, the trick is to make a fake port that consists of an open paren, the original port, and a close paren; then you can read one S-expression out of that fake port. About 10 times faster than just reading expressions one at a time. Strange but true!

I also may have fixed a couple of very small but (at least in one case) extremely annoying PLaneT bugs. It has to do with the installer, though, so I'll have to wait for the nightly build before I can test it.

Afterwards, dissertation defense. Defense-tastic, that's all I'm going to say about that.

Okay, time to fix the speed daters.


Today was mostly a catch-up day. In the morning there was much Web Page Editing done concerning my and my advisor's ICFP submission — he made a big performance improvement to Redex but it broke backwards compatibility, so I needed to update all the sample code from our paper to make it work. In addition, Redex is now distributed via my package management system, PLaneT; the instructions got changed too.

After that was Adam's jazz concert. Swank!

Afterwards there was work done on the Programming Contest. That cannot be talked about too much, so I'll just say that it was done.

I've been thinking more and more recently about the concept of a persistent-set-based functional programming language. I have over the last several years found that continuations are a fundamental idea behind web programming; over the past couple years of actually implementing web services, I've come to the opinion that persistence is another one. When you write a web service, nine times out of ten you're going to be writing a program that has some kind of sequential programmatic logic, but ninety-nine times out of a hundred its job will be either to read data from a database, write data to a database, or both. Fundamentally, the notion that's being captured is that there's some information independent of any program to which a web application is giving you some kind of interface.

I think this is actually more important than continuations for web programming, as demonstrated by the truly staggering array of web technologies that do nothing or virtually nothing to address the continuation problem but that nail database access (here ColdFusion is the earliest example I can think of, but by no means the only). We know that for control, continuations are a natural and powerful way to model the phenomenon of web interaction, but is there anything better than glorified SQL syntax for modeling persistence?

My guess is that the answer is that a programming language should have strong support for tuple sets as values, and that those tuple sets should be naturally persistent and program-independent (but be able to store any kind of value, including functions and continuations, anyway). Python and Lua, among probably dozens of others I don't know about due to woeful ignorance, are based on the notion of a hash-table as the fundamental value around which the language is based; what would a language based around persistent tuple-sets look like?

There are bunches of languages like PL/SQL around that have answers to that question, but they're not particularly satisfactory. For one thing, they're centered around the database; I'm more interested in sets as sets (with database backing) rather than databases as the inspiraton for semantics. For another, I think that sets could be treated completely functionally except for the points at which they are remembered.

I don't know whether this idea can be done in Scheme, just because Scheme has already got so much listiness built-in already. Furthermore, I think for web programming a typed language is the right choice -- unit testing is difficult, XHTML is a complicated datatype, and programs rarely need the extra expressiveness you get in an untyped language. I don't know where all this is going, though, and I don't have time to go there right now. But it's something to think about.