Monday, August 31, 2015

Thoughts on Clojure, Page Two

WTF? You didn't mention Clojure at all!

Perhaps after my prior “six months” post people were expecting more vitriol. Or adulation. Sorry to disappoint.

I really can't find much to say about the language itself, positive or negative. I'm not turned off by the number of parentheses, because I simply see them as doing double duty; “C-family” languages split the job with braces. I find prefix notation somewhat hard to read, coming from an English-language subject-verb-object background, but that's easily solved by making lots of helper functions. Parameter ordering in the standard library seems haphazard, and there are some edges to the sequence abstraction that I stumble over. But overall, meh.

So rather than write about the language itself, I decided to write about attitudes. I was prompted by the book that I used to learn the language. Overall, I think it's a good book, and I'd recommend it to someone learning the language (and I still use it). But the authors are a little too focused on the word “concise.” At one point (table 9-1), they compare Java to Clojure and make the claim that Clojure is “certainly more concise” than Java, even though almost all of their examples use the same characters — just re-arranged. Examples like this perpetuate the stereotype of the “smug LISP weenie.”

I can understand why LISP was considered revolutionary back in the 1970s and 1980s. There was a generation of programmers that learned BASIC on a DEC mini, and I can imagine them going off to college and seeing a whole new world: scoped variables! first-class functions! dynamic dispatch! structuring a program as list transformations!

Actually, I don't have to imagine that, I was part of that generation. I happened to go to a college that focused on strong typing and provably correct programs, with PL/1 as the implementation language. Which is one reason why my degree, when it finally came, was in History and not Computer Science.

But that was then, this is now, and most mainstream languages have features that were once revolutionary. Indeed, one of the reasons that Clojure exists is that it leverages the JVM, which natively supports the garbage collection and dynamic dispatch that underly any LISP implementation. But those same features are also available for Java.

So, to proclaim Clojure “better” due to its language constructs seems to me as misguided as proclaiming HP calculators better because they had RPN.

That said, I think there is something revolutionary happening in the Clojure community, and that's the view that program state must be considered in relation to time. That, to me, has far more relevance than whether you use braces or parentheses, or where you put the operator.

Friday, August 28, 2015

Thoughts on Clojure After Using It For Six Months

When I was in high school, in the late 1970s, “pocket” scientific calculators were hot. They were portable computation devices that hadn't even existed a decade prior, were incredibly expensive when they came out, but were now reasonable birthday presents for the budding geek. There were several manufacturers, but the only ones that mattered were Texas Instruments (TI) and Hewlett Packard (HP). The cool kids all had HPs.

There's no question: HP calculators were better. A TI calculator was about half the price of an HP calculator with the same features, but it reached that price point by cheaper build quality. You could drop an HP calculator down a flight of stairs without ill effect, but a TI would start to have keyboard problems within a year of normal use. In fact, that's how I got my first calculator: my father's TI stopped working, he sent it to TI for repair, but bought another before it returned.

Another difference between TI and HP was that the former used algebraic notation: “1 + 2 =” while the latter used reverse Polish notation (RPN): “1 ENTER 2 +”. From the perspective of a calculator designer, RPN makes a lot of sense: algebraic notation requires more work, and the transistors that performed that work could be put to better use elsewhere. Or put another way — one intended to taunt an HP-wearing geek — RPN-based calculators were less advanced than algebraic calculators.

Here's the interesting part: the vast majority of those HP aficionados were convinced that RPN was the reason that HP calculators were better.

Friday, August 14, 2015

The Carburetor: Elegance and the Real World

The carburetor is a simple, elegant device for providing fuel to a gasoline engine. So simple and elegant that it was the primary mechanism for doing so for approximately 100 years, and remains the primary mechanism for small engines such as lawnmowers.

Here's a simplified explanation of how it works: all air entering the engine flows through the carburetor; the “jet,” a small tube connected to the supply of gasoline, is exposed to the airflow; as air flows through the carburetor body, it pulls gasoline out of the jet and vaporizes it. The more air flowing past the jet, the more fuel is drawn into it. By properly sizing the jet, you can approach a near-optimal fuel-air mixture — although more on that later.

The airflow through a carburetor is caused by the intake stroke of the engine: as the piston moves down in the cylinder, it creates a vacuum that pulls air from the intake manifold; the faster the engine turns, the more air it wants (and the more fuel it needs). Now here's the interesting part: there's a valve inside the carburetor, attached to the accelerator pedal: when your foot is off the pedal, that valve is (almost) closed; the engine can only get enough air (and fuel) to run at idle speed. When you push the pedal to the floor, the valve opens wide, and the engine gets all the air and fuel that it wants.

But that leads to the first problem with a carbureted engine: the feedback loop (and lag) between opening the throttle and actually going faster. At a normal cruise speed, the butterfly valve is only partway open; the engine doesn't get all the air that it can handle. When you open the throttle, that lets more air into the system, and indirectly, more fuel. More fuel means more power generated by the engine, which (given constant load) means that the engine will speed up. Which increases the vacuum, which means that more air is drawn into the system, which means that more fuel is provided with it, which …

But, as I said, there's lag: when you first allow more air into the system, the flow of gasoline doesn't keep up. So at some point carburetor designers added an “accelerator pump”: an actual pump that squirts extra gasoline into the airflow. Most of the time it does nothing, except when you push the pedal to the floor; then it sends that extra gasoline to the engine, to compensate for the flow through the main jet.

While the single carburetor works fairly well for small engines, it doesn't work so well for large engines that have widely differing airflow requirements between idle and full throttle. So, carburetor designers compensated with multiple barrels: separate paths for the air to follow. At idle the “primaries” provide all the air and fuel; under full acceleration, the “secondaries” open to provide vastly more air and fuel.

All of which works fairly well as long as the altitude doesn't change. A typical automotive carburetor is sized for its market, on the fairly reasonable assumption that drivers stay close to home. But take an east coast car, tuned for driving at sea level, up into the mountains of Colorado, and the mixture becomes excessively rich: the carburetor allows too much fuel to mix with the air, and (perhaps surprisingly) performance suffers. Eventually the spark plugs will get coated with a layer of soot. A worse fate meets the Colorado car driven to the shore: it doesn't provide enough fuel, which causes the engine to run hot, which eventually causes extensive (and expensive) damage to the engine's valves and pistons.

Light aircraft, which cover a similar altitude range on every flight, have a solution: the mixture control. After every significant change in altitude, the pilot has to re-adjust the mixture to match the air density at that altitude. In a typical long-distance flight, the pilot might change throttle settings three times (takeoff, cruise, landing) but adjust mixture a half-dozen times or more. Not something that the typical automobile driver would want to do (or do particularly well; all teenagers' cars would be overly rich “for more power”).

The simple, elegant carburetor is no longer so simple or elegant: to meet the needs of the real world, it's grown a bunch of features. These features expand what I'll call the “conceptual model” of the carburetor. A simple function relating airflow velocity and fuel delivery is not sufficient to implement the real-world model.

Today we use fuel injection in almost every car (and in most high-performance light aircraft). Fuel injection systems are certainly not simple: a computer decides how much fuel to inject based on inputs from a plethora of sensors, ranging from ambient temperature to position of the accelerator pedal. But fuel injectors do a much better job of providing exactly the right amount of fuel at exactly the right time.

And, although a fuel injection system is more complex than a carburetor, its conceptual model is actually simpler: there's a single sensor that measures the amount of residual oxygen in the exhaust, and the computer attempts to optimize this value.