From: "andrew cooke" <andrew@...>
Date: Fri, 22 Dec 2006 19:31:24 -0300 (CLST)
I should add at the start of this that I find Lisp way cool, especially the macros (although I would prefer a (static) typed system). However, comments like this - http://www.lambdassociates.org/studies/study06.htm - really bug me. They give me the strong impression that the author has only ever learnt to program well - or has only seen good programmers working - in one particular language. And so they conclude that the language, whatever it happened to be, is somehow special. From there it's just a couple of small steps - some random piece of folk psychology, the always reliable appeal to the audience's sense of being better than their peers - to a 'cult' article. In my experience, life's not like that. I've seen good programmers working in C. And I've written bad programs in Lisp. Here's an example from my work. It's Java code, which means it should be sucky sucky stupid boring, but, personally, I think it's pretty neat. It's moderately declarative, and makes what was a rather tricky problem (not "hard", but fiddly) easy. private ContentFilter getHandler(SqlLoader loader) { // these store values; they are read/reset by ProposalHandler Text firstName = new Text(); Text lastName = new Text(); Text email = new Text(); Text affiliation = new Text(); Period period = new Period(); Test periodTest = new Test() { public boolean test(String uri, String lName, String qName, Attributes atts) { return null != qName && qName.equals("parameter") && null != atts && "proprietaryPeriod".equals(atts.getValue("noao:type")); } }; // this is reset by ProposalHandler for each new proposal First firstInvestigator = new First( new Parallel( new QName(Match.ALL, "first", firstName) ).and( new QName(Match.ALL, "last", lastName) ).and( new QName(Match.ALL, "email", email) ).and( new QName(Match.ALL, "affiliation", affiliation) ) ); return new QName(Match.ALL, Forward.ALL, "proposal", new ProposalHandler(salt, loader, firstInvestigator, period, firstName, lastName, email, affiliation, new Parallel( new QName(Match.ALL, Forward.ALL, "investigator", firstInvestigator) ).and( new Element(Match.ALL, Forward.ALL, periodTest, period) ) ) ); } If you know some XML, and some Java, and some Haskell, you may have guessed that this code is using something rather like a combinator library for processing XML. Before I explain the code in more detail, I'll give some background. We needed to populate a database from some information in an XML file. There's no real constraint on how large the file may be. It's hand-rolled, unvalidated XML, with no DTD or Schema, contains way more data than we need, and is very over-constrained (you can may decisions locally within the file on what a particular element is). The XML file is structured so that information for each "proposal" is in a distinct subtree; I already had a set of classes that loaded data for a proposal into the SQL database. The problem, then, becomes one of (a) selecting values from appropriate XML elements/attributes, and (b) calling SQL loader for each proposal. I chose to solve this using SAX - a library that converts an XML file to a sequence of events - effectively an in-order traversal of the DOM tree, but handled sequentially so that the whole XML document does not have to be in memory at once. This suggested that a library that allows the user to define filters that select particular sub-trees within the XML document. These filters could then be nested to isolate particular paths in the document, without having to specify the full path - only the relative position with the tree need be used. This approach is resilient to changes/errors in the (unvalidated) document format, and allows test data to be written in a more compact form. To take a concrete example, the fragment of code... new First( new Parallel( new QName(Match.ALL, "first", firstName) ).and( new QName(Match.ALL, "last", lastName) [...] ...selects nodes relative to some parent node such that (i) only the first (First) child node of the parent is considered and (ii) under that first child node, at any depth (Match.ALL) an element with the name "first" or (Parallel) the name "last" would trigger the appropriate action (firstName is a Text instance and stores the text value of the element). Clearly this is stateful. ProposalHandler subclasses the library class OnEntryExit and has methods that clear state on entry and load values to the database on exit. The ProposalHandler instance is embedded in a filter that tests for the "proposal" element. This is not the world's greates code, I admit. But for a project with busy programmers and tight deadlines, I think it's pretty neat: the description of how the XML data are accessed is collected in a single method, with no duplicated literal values, in a form that shows the logic used. Of course the code above is strongly influenced by combinator libraries. But the implementation is idiomatic Java - classes, not higher order functions, are used. The point being: an educated programmer can do the job well in any language. Ideas for how to approach a problem can be separated from the language used for an initial implementation; they can be implemented elsewhere if you are fluent in the target language. Which explains my title: my suspicion is that people who write screeds about how wonderful a certain language is are fluent only in that one language. In particular, there seem to be a lot of whiny academics complaining (LtU) about Java rather than putting in the hours to become adept at using it. It's just a tool. Andrew