I used to joke that I was the only programmer under 30 who knows SNOBOL. It may not be the case anymore, but there’s something fun about knowing a bunch of obscure languages. For example, a used Prolog book on Amazon goes for $20. A used Smalltalk book goes for about $10. A used SNOBOL book goes for about $1. For comparison, used books on functional programming usually seem to go for about $80. I have a ton of those books, and I haven’t learned as much from any of them as I have from Real World Haskell or ML for the Working Programmer, which are both still in print.

It’s hard to sing the virtues of languages like SNOBOL, but a bit of context helps I think. For one thing, SNOBOL stands at a certain evolutionary dead end. They don’t make languages like that anymore. It’s shamelessly GOTO-laden, has a laughable approach to error handling, and its handling of strings is quite strange by modern standards. But I think it really exemplifies the fact that nobody knew what the hell was going on thirty to forty years ago.

I was reading an old issue of BYTE magazine that I downloaded. It was the August 1981 issue, the issues from my birth month, which happened to be the Smalltalk spectacular issue, with four or five articles about Smalltalk. When you invest in an unpopular language with a long history like Smalltalk, Prolog or Lisp, there’s always this sense looming in the air that you’re wasting your time or that the language is defunct. This notion is absurd; the internet ensures” that no language can die, the worst they can do is lag behind, and this will be treated like a major problem whether or not it is true.

In fact, what struck me about this issue of the magazine is the gulf between the content of the magazine and the ads alongside that content. The Smalltalk authors are essentially trying to explain what a graphical user interface is to people who have never seen them before, and right next to the screenshots of windows and menus with Copy and Paste in them are ads declaring that this $1700 graphics card will enable you to have character art in 16 colors on your Sinclair. There’s an ad for a text editor for $300. There’s ads for 16K expansion cards, tape drives, build-it-yourself computers from companies you’ve never heard of. It’s chaos and confusion. Smalltalk must have looked like an absurd dream to most of the readers. Something that couldn’t be, adjacent to an ad for a cassette drive or business software with runes in the logo.

Against this backdrop, the sins of SNOBOL are a bit easier to explain. Issues of this and other computer magazines—remember, we’re talking about just 30 years ago, not a hundred—would often contain source code” for a program, in some kind of assembly language. There were easily half a dozen competing architectures of computer running around, all for similar costs. In this same magazine there are ads for compilers in BASIC, Pascal, Fortran, Forth, and C, all for different combinations of platform. There are ads for third party operating systems, including one I’ve never heard of that implemented a very basic Unix on the Z-80. In this context, the sales pitch for SNOBOL looks like this: it’s portable, it’s for string processing which you’re probably not enjoying in one of the mainstream languages of the day, and it resembles assembler. For that matter, it’s also probably quite fast compared to BASIC, which is probably what you were using before.

It’s a little disingenuous to look at the situation today and say C’s dominance is based on some kind of obvious superiority when Pascal and BASIC were so incredibly prevalent during this time period on personal computers. The honest answer is probably happenstance. So if the world we live in today, computing-wise, is mostly a result of happenstance, then the past ought to be brimming with interesting stuff long forgotten. Unfortunately, I’ve steeped myself in this mythology so thoroughly it’s hard for me to tell what’s appreciated and what’s been thrown away.

One thing I think all programmers should put more effort into is having some patience for learning older stuff. The internet naturally predisposes us to be excited about exciting new technology, not dusty old technology. But there’s a tremendous amount of group-think going on in the world today. Things which don’t have to go together are always seen together, and interesting alternatives require a celebrity programmer or a heavily marketed implementation to get traction. For example, the next version of JavaScript is going to have classes, for no compelling reason. NoSQL databases are taking off, despite the fact there are no credible non-SQL relational databases. That whole fiasco is very much 1960s style network and hierarchical databases recast with web jargon; all the old relational database arguments work against them, yet neither side has the historical knowledge with which to lead the debate.

I see the industry as having entered a sort of river delta in this era. Very little software technology truly dies these days. Mostly, weaker offerings (mSQL, APL, Joy) are absorbed by similar offerings that, for whatever reason, achieve popularity (MySQL, J, Factor). Since so much of this stuff is open source and unsupported commercially, when one maintainer leaves another one steps up. You can program in MUMPS today mostly because a handful of huge medical apps are so it is supported. Ada hasn’t had a new book published in several years, but it will continue to be used in aviation and learned for that reason until something stronger comes along.

Perhaps what’s needed is not to provide enough context that Smalltalk, SNOBOL and so forth can be appreciated by modern readers. It may even be an unhelpful distraction. After all, people have shit to do, that’s why they need to program in the first place. Perhaps it would be more helpful to skip the plea for lenience and dive right in, pretending that the inconveniences are unnoticeable because they usually are in practice. After all, there are a lot of great ideas in Clojure, but I pinch my nose at the Java-isms. Today, these are convenient and helpful; in another decade, will that still be true, or will they be undesirable reminders of Clojure’s origins? Common Lisp, for example, includes a lot of crap in a file path” that makes no sense on any modern platform, like hostnames and version numbers. It’s really much closer to a primitive URL. This doesn’t stop Lisp from being useful or even its file paths from being useful, it just adds to the general funk. Similarly, Smalltalk doesn’t specify where the stuff in the image is sourced from. Modern users are put off by the inability to conveniently use Git or Mercurial to manage their versions, but this was considered a forward-thinking view and an improvement in usability over file-based programming. At the same time, Ruby is demonstrating that it’s hard to make an open class world work reliably and safely, but Smalltalk has had that figured out for some time.

It’s an interesting world.

November 7, 2011






In my experience, there are two extreme visions of reality embodied in two philosophies of programming language design. The first is Dijkstra’s view, the view embodied by Milner’s guarantee in ML, that once the program is compiled it cannot go wrong” at runtime. This perspective treasures correctness and asserts that all wonderful things flow from it: reliability, readability, and so forth. Languages supporting this view of reality are primarily Haskell and ML. I’ll call this the welded-shut view.

The second vision is the biological vision, the notion that the system is like an organism that can change and evolve over time. This view is embodied by Lisp, Smalltalk and Erlang. I’ll call this the organic view.

C is not a language I would consider hallmark of either trend. But C manages, in fact, to do a great job at permitting reliable software development. Why is that? I argue that has a lot to do with POSIX and the nature of the C/Unix standard library. In short, any function that can fail returns a status code. If it must also return values, you pass those values into the function as pointers and the function will record the results in there.

This is decidedly unsexy. But it’s a great reminder that anything can fail: memory allocation, file opening, file closing, anything really.

In the organic languages, there is a strong preference for introducing exception mechanisms and handling errors with a separate control flow. This keeps the simple code looking simple. You can still do simple things simply in C, though it gets a bit ugly with passing result arguments to functions, by ignoring the result codes of functions. You can do this, but your program won’t abort at the right time; it will probably continue to work past a malfunction.

There is a strong argument against exceptions, one I rarely see promulgated, which is that it makes life hard on you if you want to actually implement robust software. There’s nothing in the signature of the function to indicate that it may, instead of returning a value, throw you some horrible exception, and here are the types of those exceptions. However, in the C/POSIX world, it is very much part of the method’s signature that certain error codes may be returned.

Java almost went this route with checked exceptions, but it retained unchecked, runtime exceptions, so it’s sort of a lame duck. In the few places where checked exceptions are used, it feels like a hassle, because so many other things that do throw exceptions don’t force you to deal with it, so why should this part?

I find it surprising then that the welded-shut languages usually also provide an exception mechanism. If I want to make a Haskell program do the right thing when a file cannot be opened, I have to use the ioError function to catch a runtime exception that may be thrown. This, when Haskell already has perfectly worthy ways of handling situations where you may get one of two results (Either). If one wants to write truly reliable code in Haskell or even Standard ML, one must discover through reading the documentation which exceptions might be thrown in which circumstances and handle them.

If you go asking around about how one is to achieve reliability in an organic language you’ll get an earful about automated testing. Over the last weekend I wrote a smallish program in Smalltalk using TDD with the Autotest facility. I have to say, I can see that TDD would obviate a lot of the need for type checking and some other categories of problem which it would be hard to trick a type checker into maintaining. The nice thing about Haskell’s type checker, though, is that it insulates me from having to do so.

I find I gravitate more towards the welded-shut languages, but I find it interesting that neither they nor the organic languages really have found a place in industry. Most of the world uses languages like C and Java, which furnish you with only the most rudimentary type checking, which do not really allow things that have been defined during compilation to change, but which do allow dynamic loading of 3rd-party code. I wonder why this is. Traditionally, systems like Smalltalk and Lisp have had, alongside with their liveness,” a tacit expectation that if anything were to go wrong, the user is right there and can handle a condition or an exception manually. I suspect this has something to do with their unpopularity. Erlang doesn’t seem to have this weakness.

Another thing you probably wouldn’t expect is that, while I’m unaware of a method to dynamically load code into a running Haskell or ML process, there are frequently few good ways to interface organic languages with the outside world. Lisp and Smalltalk are about the only image-based languages around, they try very hard to be more than just a language and actually a whole environment. Smalltalk really takes this to the extreme, but both have evolved pretty strong walls to keep the world out. Haskell and ML are definitely not image-based, programs written in these languages interface with the OS like everything else, without creating their own walls.

It seems like there are more than a few oddities here. I would expect organic systems to be better at interfacing with the world, and I would expect ML and Haskell to avoid exception system. Instead we find that generally isn’t the case. Also interestingly, Erlang manages to achieve high reliability by accepting that processes fail and allowing it. This seems antithetical to the welded-shut approach of proving that nothing will go wrong. And indeed, highly reliable systems are written in Erlang.

  • Would a language like ML or Haskell, but without exception handling, be interesting? Would it improve reliability?

  • Why is reliability not correlated with strong, static typing?

  • Is there a reason organic systems are not written in organic languages? Or is this an artifact of rarity of use?

October 31, 2011






To assist with job interviews at the NRAO we recently wrote a small contest” program. Without giving away the details, the crux of the problem is to read a file with a list of scans and calculate the amount of time it takes to move the dishes and perform the scans, and report it. Candidates are required to write this in Java, but that restriction does not apply to me, so I of course had to write it in three languages that are not Java: Haskell, Common Lisp and Smalltalk.

I expect to learn something, or at least reinforce an existing fact, by doing these sorts of language games, but this time I really wasn’t expecting what I found. I discovered that my enjoyment of Haskell is pretty much independent of Haskell’s appropriateness or usability. In fact, it is more attributable to the way I feel when using it, in contrast to everything else. Let me show you a piece of code:

-- | Perform a scan and produce the scan log for it.
performScan ∷ Scan → AntennaSimulator ScanLog
performScan scan = wrapDuration $ do
  slewTime     ← moveTo  $ scanPosition scan  -- move to the scan's position
  onSourceTime ← waitFor $ scanLength   scan  -- wait for the scan to finish
  return $ Log scan onSourceTime slewTime 

Somehow, the other languages I’ve used to implement this problem never arrived at a piece of code quite this beautiful. Compare to the Smalltalk:

Scan ≫ runWith: anAntenna
runWith: anAntenna
  "Performs this scan on the supplied antenna."
  | onSourceTime slewTime |
  slewTime     := anAntenna moveTo: self position; lastActionDuration.
  onSourceTime := anAntenna wait: self duration; lastActionDuration.
  ^ ScanResult withScan: self slewTime: slewTime onSourceTime: onSourceTime

And the Lisp:

(defun run-scan (antenna scan)
  "Perform a scan on this antenna. Return a scan log."
  (let* ((slew-time       (move-to antenna (scan-position scan)))
     (on-source-time  (delay antenna (scan-length scan)))
     (total-time      (+ on-source-time slew-time)))
    (with-slots (last-duration) antenna
      (setf last-duration total-time)
      (make-scan-log scan slew-time on-source-time))))

Strangely, despite the similarity of the Smalltalk, I still find the Haskell shorter and clearer. It’s also more modular, though it doesn’t look like it. The wrapDuration command arranges for the next set of actions to count as one as far as the lastActionDuration is concerned, but does not interfere with usages of lastActionDuration within the wrapped action. I found this notion essentially impossible to port to the other languages. The Haskell version really feels more like a composable set of actions for driving the antenna, whereas the Smalltalk version never really stopped feeling like an arithmetic fabrication, and the abstraction leaked a lot in the Lisp version

The Lisp code is just atrocious. I was never really able to put my finger on what it is I dislike about Lisp before. It’s this: while Lisp code is often clever and interesting, it also always feels like I’m tricking Lisp into doing what I want, rather than simply expressing what I want.

Neither Lisp nor Smalltalk really lend themselves to my style. I realize now, that my style is to try to break everything down into the smallest piece, a piece that doesn’t look like work. Haskell encourages this style with the where syntax, which encourages you to say, X is really Y + Z, with Y being this and Z being that. This kind of thing:

-- | To calculate the time to move between two positions, we take
-- whichever is larger of the time to turn and the time to change
-- elevation.
timeToMoveTo ∷ Position → Position → Time
timeToMoveTo (sourceAz, sourceEl) (destAz, destEl) = 
  max rotationTime ascensionTime
    where
      rotationTime, ascensionTime ∷ Time
      rotationTime  = timeToRotate sourceAz destAz
      ascensionTime = timeToAscend sourceEl destEl

This almost doesn’t look like code to me. I would expect any programmer to be able to at least get some sense of what’s going on here, even without knowing Haskell. I don’t think I could say the same thing about this piece of Lisp:

(defun time-to-move (from-pos to-pos)
  "Calculates the total time to move between two positions, assuming
rotation and ascension can occur simultaneously."
  (with-accessors ((from-az azimuth) (from-el elevation)) from-pos
    (with-accessors ((to-az azimuth) (to-el elevation)) to-pos
      (max (time-to-rotate from-az to-az) (time-to-ascend from-el to-el)))))

Lispers are fond of talking about the programmability of the syntax and how easy it is to learn. But the syntax is still there, it’s just encoded positionally rather than with auxiliary words and symbols. For example, the words azimuth and elevation in the Lisp code denote accessor functions. The outer with-accessors macro does something akin to this:

timeToMoveFrom: from_pos to: to_pos
  | from_az from_el |
  from_az := from_pos azimuth.
  from_el := from_pos elevation.
  ...

So the Lisp is wonderfully terse, but I still feel like I’m tricking something into doing what I want rather than expressing it. I think it’s possible to write clearer Lisp code, I just don’t know how, and I’ve relied on Haskell long enough now that Haskell’s version of clarity is becoming my own version of clarity.

I think this is part of what makes my love for Haskell irrational. I really can’t with a straight face tell you Haskell is a better language than every other language I know. I can’t imagine anything more frustrating than trying to teach Haskell to pragmatic people who can already program. And my style grows ever more idiosyncratic as I use it more. (I consider most uses of monad transformers a failure of functional decomposition). I never lose sleep wondering if my code is going to break in strange ways in Haskell, even though it would, albeit probably less often.

Another thing which surprised me about this little experiment was discovering the degree to which I am dependent on the compiler or interpreter to program effectively. Despite idolizing Dijkstra and befriending John Shipman and Al Stavely, I’m lousy at analyzing my code before running it. This problem affects me in every language I use, but I think I am worse because I simply have no respect for languages other than Haskell. Haskell will catch me trying to do really absurd things much earlier in the process of my not understanding what I’m doing. With Lisp and Smalltalk, I often managed to write ten or twenty lines of code, code that parsed successfully, before realizing I was doing something completely stupid, designing myself into a corner or worse. I’m sure I could write elegant code with less waste in these languages, but Haskell really forces me to.

A good friend once said, you don’t learn a foreign language for the sake of knowing it, you learn it to read the poetry. And we live in a world in which people meticulously study Hebrew, Greek and Arabic just for the sake of reading some of the best poetry we have. It would be absurd for me to claim that Haskell is always shorter, always more readable, or whatever; there are certain things I just don’t like to go near when I’m using it (like web development). Is this not similar to preferring Biblical Hebrew for religious poetry over humor?

A lot of people use and encourage the teaching of languages like Java and C for their simplicity. My wife endured this in C, and I got to see first-hand that notions like function calling, sequential processing, and boolean logic are not intuitive. If I reach back far enough, they weren’t intuitive to me either, I just crossed those bridges so long ago I seldom think about what it was like before, but I have enough of a glimpse that when obscure languages like Smalltalk and Haskell are discarded from the realm of possibility for teaching beginners, I find it upsetting. I didn’t know anything about category theory before I learned Haskell, and I still know about that much, yet I am able to benefit from writing terse, correct programs in it, programs that I can reason about the performance of. Granted, my understanding of Haskell’s execution model is mostly in terms of how it differs from C and every other language. But I still remember learning things about C’s execution model that were shocking and amazing, and I still managed to learn and be productive in C before I understood those things, so why would it be different with Haskell?

I want to have a concise, cogent, interesting conclusion to insert here, but there really isn’t one, because programming is not a destination and all of my conclusions are provisional. The more I learn, the more discomfort there is. For one thing, if Haskell is so wonderful, why is there no good dependency management? Why do languages like Lisp and Smalltalk depend so much on manual intervention with mistakes, why can’t they have a strong, inferring type system like Haskell? More importantly to me, why am I never really able to commit to an environment like Smalltalk that embraces code evolution and failure, which seems like something I would like, and why do I like a system that tries so hard to achieve compile-time correctness when I depend on rapid iteration to write code?

One promising option I am looking forward to playing with more is Autotest, which detects which tests should be run after each change of the code and runs them, used in conjunction with TDD. This could potentially go much further than strong typing, but what if I’m testing something that has to do I/O? A question for another day.

October 30, 2011






I have been using Smalltalk, specifically Pharo Smalltalk, a derivative of Squeak, for some light experimentation with Seaside and proper object-oriented programming.

I really do spend too much time being a philosopher of programming and not enough time actually programming. On the plus side, I view this with some derision as a fault.

I realized last year or so that all the functional programming in the world wasn’t going to make me adequate at OO design, and I really haven’t advanced at that skill since the middle of college. I got really into Ruby in about 2003, but by 2005 I was more-or-less in the FP camp. Well, now I’m in the delightful position, thanks to doing Java professionally and Haskell amateurly, of having not advanced at OO design or FP design.

I maintain that Smalltalk is the only OO language that can really teach OO design. This is not a new opinion to the world, but fairly new to me. I see the breeze with which Smalltalk APIs seem to become clear and easy, and I have to say there’s something sublime going on that I can appreciate but not yet perform. I would like to be able to perform it, though, because my attempts at designing nice OO interfaces at work are very poor. My boss does much better, although he has the benefit of having been an early adopter of OO and of Java, and he never was distracted by FP or any other development in the annals of CS, whereas I’m usually distracted.

None of this is getting me any closer to a reasonable point.

Illustrations of the Power of Smalltalk

Procedural Code in Seaside

All of Seaside is fairly impressive, but one thing that caught my attention has been the ability to port scanf/printf-type C code over to Seaside without much change in flow control. My wife is taking CSE 113 at NMT, and for fun I have been doing my own versions of her lab assignments in my weird languages like Smalltalk and Prolog. I scratched my head for a good several minutes before realizing that it would likely be fruitless to try and implement console I/O from Pharo, it would be far simpler to implement a simple Seaside web-app instead. And indeed, this kind of code:

int guess;
printf("Enter a number between 1 and 100: ");
scanf("%d", &guess);
if (guess < generated)
  printf("Too low!\n");
else if (guess > generated)
  printf("Too high!\n");
else
  printf("You got it!\n");

ports quite easily to Seaside:

| guess |
guess := self request: 'Enter a number between 1 and 100: '.
(guess < generated)
  ifTrue: [ self inform: 'Too low!' ].
  ifFalse: [ 
    (guess > generated)
      ifTrue: [ self inform: 'Too high!' ]
      ifFalse: [ self inform: 'You got it!' ] ].

Apart from my inability to know the right way to do a case/switch type thing in Smalltalk, it’s a fairly straight-across port, except now it’s displaying form entries and data on a web page and reading from them.

Interactive Debugging Like No Other

Having a bug in your Seaside code is quite fun to debug, at least initially. If you see the traceback in the browser, you can hit Debug” and find yourself in the Smalltalk debugger sitting on the line of code that blew up. This is like most any other debugger in other languages; you can move around in the stack, inspecting objects. Unlike other languages, you can also change the code in the debugger, hit save and hit proceed or restart, and possibly get further in running your code without having to restart the servlet or even begin a new request, necessarily.

Crazy as this sounds, it’s not the limit. I ran the Magma server from a workspace and accidentally closed the workspace, losing my variable with the reference to the server controller. I ran the Process Inspector to see what threads were running. Found a bunch of Magma processes but not a reference to the server controller. I learned at that point, though, that you can actually inspect the whole stack of any running thread, anytime you want.

Next I discovered you can send allInstances to any Smalltalk class, and it gives you an OrderedCollection of instances of that class. I inspected that and was able to inspect my way to the server controller I had instantiated, and I could click in the little message window and send it messages. This it crazy and hot, and I don’t know of any other system with this kind of power, apart from Ruby with ObjectSpace, but it’s not even this cool.

March 17, 2011






Today at work, while dealing with Hibernate, we noticed a queer problem that comes up when using one-to-many mappings. Like everything else in Hibernate, the system is apparently self-consistent, until it isn’t, and I usually find I can get about 90% through an explanation of why Hibernate requires something, but when I get to the last 10% I realize there’s a missing step in my understanding, or an unsound leap of reasoning in the software.

At any rate, what we’re trying to do is put ON DELETE CASCADE on all our foreign keys, but Hibernate only supports on-delete="cascade" on inverse mapped one-to-manys. This turns out to suck because in order to have this, your relationship must be bi-directional, and in order to be bi-directional your relationship must be between entities that refer to each other.

This reveals a mistaken world view in Hibernate, which is that you can somehow achieve all three of these goals simultaneously:

  1. You can support legacy databases
  2. You do not require changes to the Java code for persistence
  3. Every column you care about maps to a Java bean property or field.

In this case, the problem is revealed by the fact that Hibernate’s on-delete="cascade" feature depends on inverse mapping because otherwise Hibernate manages the collection from the parent’s side. But databases do not have parents and children nor do they have collections. A one-to-many relationship in a database is just a foreign key shared by many rows in one table, pointing to a primary key in another table with as few as one row. Looking at it as a collection of any kind is a mistake, but it’s a mistake foisted upon you by Java.

Hibernate layers certain defined meanings onto several ways of using a one-to-many relationship by way of <list>, <set>, <bag> and so on. In reality, relationships between tables aren’t so well-defined, but Hibernate needs this information to work its magic for some reason. At any rate, Hibernate achieves support for lists by, in general, first inserting the list items and updating them with their sort order and then applying their foreign keys, like so:

INSERT INTO parents (id, ...) VALUES (?, ...);

INSERT INTO children (id, ...) VALUES (?, ...);
INSERT INTO children (id, ...) VALUES (?, ...);

UPDATE children SET parent_id = ? WHERE id = ?;
UPDATE children SET parent_id = ? WHERE id = ?;

This works well enough in databases with poor relational integrity, but in a real database such as Oracle or PostgreSQL, these columns will often not be nullable, so you’ll have a constraint violation on the first INSERT into the child table. Well, as it turns out there’s a way to get Hibernate to do this the intuitive way. Change this:

 <key>
   <column name="parent_id"/>
 </key>

Into this:

<key not-null="true" update="false">
  <column not-null="true" name="parent_id"/>
</key>

This is confusing for one of the most annoying of reasons: there’s an update attribute for many-to-one which means something completely different than it does on <key>, and the not-null attribute has similar but subtly different meanings on <key> and <column>, a fact exacerbated by the fact that you can often merge the two and further complicated by the fact that you almost always want to index this column, which you can’t do by merging the elements because <key> does not take an index attribute. (Also note that there’s an <index> element that goes inside a <one-to-many> as well. This chart summarizes the differences:

<column> <key> <many-to-one>
not-null=“true” Add NOT NULL to column definition Mandates this primary key at insertion time
update=“false” Mark this foreign key column as non-updateable Half of marking this relationship as virtually inverse.”

Now you may ask, what’s this virtual inverse there? Well, as it happens, Hibernate doesn’t support inverse="true" on many-to-one relationships, so instead you say <many-to-one update="false" insert="false" ...>, which is apparently considered the opposite of <one-to-many inverse="true"...>, because by disabling UPDATE and INSERT statements, you’ve effectively nullified the management problem for Hibernate (but it still counts for bi-directional mapping purposes).

Note that in that case, update="false" means something very different from what it means under <key>. The combination of update="false" and not-null="true" in <key> tells Hibernate that the parent key is needed at insertion time and that it cannot be fixed later with an UPDATE.

Another thing to note is that once you have <key> and <column> you can set not-null= to different values, yielding this chart of possibilities.

<tr><td>not-null="true"</td><td>not-null="false"</td><td>Hibernate
does an insert and no update, but doesn’t make the database add a constraint to check for this situation.
<key> <column> Effect
not-null=“true” not-null=“true” Works fine.
not-null=“false” not-null=“true” Broken: Hibernate thinks it doesn’t have to insert the row with the foreign key column, but the RDBMS is requiring it for data validity because Hibernate added the constraint.

Back to Philosophy: Mixed Metaphors

Zed Shaw wrote another beautiful manifesto this last week, calling for us to embrace Programming, Motherfucker. I tend to agree with it, insofar as methodology is never a silver bullet, there are no silver bullets, and so forth, but I can’t accept it if its promulgated as a strain of anti-intellectualism. None of Hibernate’s problems are due to a lack of programming. They’re all due to a lack of philosophy.

Is Hibernate a framework for persisting an arbitrary object model into an arbitrary database? No, because if it were, Hibernate would not have opinions about the kinds of structures in relational databases they support. There would never be a dismissive post from Gavin King or one of his cronies, explaining to the poor RDBMS exile that Hibernate doesn’t support this capability, fuck you for asking, and what kind of moron are you anyway? Looking through the hype, when you search for help on any particular topic with Hibernate, you will invariably find three or four borderline-relevant forum postings in which the diligent user is dismissed for doing something non-standard in their database and told to fix it or fix the object model. Hibernate is most certainly not a framework for supporting legacy databases.

At the same time, Hibernate is not particularly a framework for generating databases based on an object model. For one thing, you’re required to specify, either in annotations or in XML, the precise structure of the database to which you want to connect. Hibernate has tools for generating the database from there, but they come with a strong warning to the effect of, these are for development, not migrating production” and a pointer to Liquibase.

Hibernate pushes itself as being unintrusive into your domain model, but the inability to support the database feature of cascading deletes in the absense of bi-directional links in the model is a prime example of a situation where these two things that are supposed to be separate and invisible to each other are actually intertwined in a very complex way. It’s more fodder for the ORM restatement of Jamie Zawinski’s celebrated regular expression axiom: you see an object model and a relational database and say to yourself, Oh, I’ll use Hibernate.” Now you have three problems, and each one of them is literally a world unto itself.

Not to cut Hibernate any slack, but really, how can you support all of these ideas at the same time? How can you abstract the structure of the database, the structure of the object model, database querying, and the database engine itself at the same time without making gross assumptions? I’m not sure you can, but doesn’t most software live in this sticky realm between the hard and fast rules?

Doesn’t that explain some of the oddities though? If Hibernate were mainly about mapping objects to legacy databases, wouldn’t it mainly use database terminology? If it were mainly about mapping legacy objects to self-created databases, wouldn’t it mainly use Java terminology? Instead the terminology is a weird hodge-podge, and you wind up with lots of cases like update= above, where the configuration isn’t really using jargon from either domain or being particularly abstract or declarative. What it’s doing is giving you control over some implementation detail, some piece of minutia in Hibernate’s process. This, like all situations in application design where you rely on configuration or preferences too much, is an indication that the author didn’t understand the user. And in this case, the reason is just that Hibernate sits at a particular intersection of the three variables above and each developer has their own slant, their own idea of what abstract” would be, and this just turned out to be the only wording the devs could all agree on.

Another symptom of this is the virtually inverse” debacle, where it takes two properties to achieve one effect. This one’s made even funnier by the fact that Hibernate requires that both the insert and update properties have the same value on a <many-to-many>. So you have to set two settings to the same value whose names bear no resemblence to the effect you’re trying to achieve, and better yet, Hibernate statically determines whether you’ve broken the rule and will reject your configuration!

I’d like to nail Hibernate for being shitty at mapping complex object models and legacy databases both, but the truth is that Hibernate does an acceptable, if not great, job at both, provided you invest enough resources into it. I just happen to believe that the amount of resources you need to invest far exceed the benefit, but not just for philosophical reasons. I believe it also for practical reasons:

  1. You never need to support a legacy database and a database generated by Hibernate at the same time. (If you think you do, what you actually have is a legacy database that you’ve allowed Hibernate to continue making changes to, which is not a great idea).

  2. If you do need to support a legacy database, you don’t also need RDBMS portability. (Why would you need to be able to generate the legacy database’s schema when porting to a new RDBMS?)

  3. Writing your own home-brew data storage wrapper is time consuming, but the profile of the work is a medium-sized amount up-front and then a small amount spread over the life of the project. Using Hibernate is more like a labor time-bomb: you get off the ground running, but eventually a day comes when you have to master Hibernate and fix your mapping. On that day, you are forced into spending as much or more time learning this complex system.

  4. Hibernate’s mapping is brittle and depends on precise cooperation with the object model. For example, we had log messages being created and discarded in our object model that were causing Hibernate to load lists during object instantiation. It turns out, doing this can cause Hibernate to load the collections, but because it happens during another load, Hibernate ignores its configuration settings and loads them one-by-one instead of using outer joins or subselects per the configuration. In other words, we were having Hibernate performance problems due to unsuspicious looking log statements that weren’t even printed.

Problems like #4 are easy enough to avoid if you know a little bit about how Hibernate works, but the whole point of Hibernate is that my boss doesn’t have to learn anything about it, it will just handle the database and he won’t have to mess up his data model. But this can’t really be true, so you wind up having to learn the right way” to write your object model, which amounts to breaking a lot of your preconceived notions.

Missing Context

Every bad decision Hibernate made was to solve some problem. It’s just that in many cases the solutions that came out of facing those problems were constructed in a different context.

Here’s a (haha) simpler example: JSF and Facelets. A Facelet file is an XML format that defines a component tree, in other words, a tree of UIComponents, which integrate into the JSF framework for processing web requests. It’s a web framework, in short. A facelet looks a bit like this:

<h1>Facelet Example</h1>
<p>Here's an example of an input control:</p>
<h:inputText value="#{object.name}"/>

Now, what JSF does for you is build the UIComponent tree, and then whenever the form on this page is submitted, it makes sure that the value in the inputText component there gets propagated back to the name property on the object managed bean. In practice, it means that JSF is going to find an object, probably in your session but also possibly in the request or globally, and then it’s going to use JavaBean-style reflection to get a setName(String name) method and call it with whatever text wound up in the inputText tag. It’s also going to populate the field with the result of calling object.getName().

Seems pretty cool and flexible, right? And not too far off from Smalltalk, where you often see code that looks like this:

builder newTreeFor: self list: #elements selected: #selectedElement
changeSelected: #selectedElement:.

This tells some builder instance of UITheme to generate whatever the current theme’s implementation of a Tree component would be, and tells it to get its data, it should (essentially) invoke self elements to get the list of elements, self selectedElement to get the currently selected element, and self selectedElement:anElement to set the currently selected element, of course self being a pointer not to the builder but to the containing class. That’s the whole interface for this particular UI widget: three methods. So to use a tree widget in Polymorph, one doesn’t even need to define methods with certain names or implement an interface to integrate with the widget. You can just tell the widget some callback methods.

So you can see JSF inherits some of this philosophy. Rather than making you build a component hierarchy in statically-typed Java code, you can just assemble the facelets in a facelet file and avoid recompilation. You can give the code to your designer, they can move stuff around, proof it in their own browser and give it back to you when it looks good. Sounds pretty fun, right?

The problem here is, again, philosophy: interpreting a string or symbol at runtime to find an object and invoke a method on it is extremely useful in Smalltalk, but if anything goes wrong, you’re in the debugger already, you can fix the code and hit Proceed” without stopping or restarting anything. In Java, if the facelet’s calling code I haven’t yet written, there’s no compiler error, but to fix it will require recompiling. Deploying a web app necessitates a full recompile/redeploy cycle, which takes about a minute. By introducing late binding into Java with JSF at the view level, we’re not adding a helpful bit of dynamism to stark statically-checked pain, we’re driving a wedge between the view and the model/controller completely. We’re nullifying the gain of static type checking without nullifying the cost. In other words, it would have been better to pay the price of static checking all the way through and not be able to compile an app with bad facelets views, or better to just jump ship altogether and use Smalltalk or some other dynamic language with a dynamic framework on top.

What’s interesting to me about this is that, in a sense, the solution is both. There’s no excluded middle here. Sometimes dynamic is better, other times static, but it would be easy to couch an argument for one or the other in logical-looking terms that would seem to prove” that the other one is utter shit. And that’s why I think perhaps philosophy ought to be looked at a little more closely in programming. It seems to me there’s going to be room for Smalltalk and Haskell for a long time to come. If such wildly different things can exist simultaneously and provide usefulness to, in some cases, the same people solving the same kinds of problems, what use are the philosophical underpinnings?

March 6, 2011