Tuesday, November 25, 2008

Cabal is a fine piece of software

Many people in the Haskell community use a build-and-packaging library called Cabal. I used Cabal long ago, but that was in a simpler era. These days, I’ve found that a batch file that reads roughly:

@ghc –-make –O –hidir obj –odir obj –o target Main.hs
works just as well. However, I’m now part of a larger project in which the other members are using Cabal, and it’s given me an opportunity to appreciate how nice development with Cabal is these days. I thought I’d provide a couple of examples.

One nice thing about Cabal is that it really helps you appreciate the different parts of the build process. For instance, suppose that I change my cabal metadata file, and then attempt to build my project, for instance by saying
runghc Setup.hs build

Cabal will helpfully reply:

Setup.hs: interpreter.cabal has been changed, please re-configure.

Notice how Cabal has helpfully here noticed that there’s work to be done, and not done it. I found that this really helped me appreciate the details of the build, rather than unhelpfully hiding those details behind some veneer of “automation”.

I noticed another nice Cabal feature upon first adding the line
import qualified Data.Map as Map
to my program. Of course, recompiling produced the message:
Preprocessing executables for interpreter-0.1...
Building interpreter-0.1...

src/Core.hs:8:17:
Could not find module `Data.Map':
it is a member of package containers-0.1.0.2, which is hidden
This actually illustrates a couple of nice things. First, I really like the message about preprocessing executables. I haven’t said anything about preprocessors in my Cabal metadata file, but Cabal is helping me to realize that perhaps I could have. Or perhaps it’s telling me that it has to do some preprocessing as part of the build, even if I haven’t told it about preprocessors. This is good knowledge to have about the build process

Second, I’d like to highlight the line “building interpreter-0.1”. Cabal is actually building a file called dist\build\interp\interp.exe. But it’s not confusing me with that – rather, it’s reminding me of the project name and version I defined in my Cabal metadata file!

But really, my favorite feature is highlighted by the GHC error message about not being able to find Data.Map. Of course, Data.Map is on my system – GHC’s even found it, and told me where it is. But Cabal’s made an important distinction: just because I have software installed in my development environment doesn’t mean I want to use it! Rather, Cabal’s started out by hiding all the Haskell libraries installed on my system. This helps me really appreciate the packaging system, as I get to individually add each package I’ve previously installed and then wish to use to my metadata file. Of course, keep in mind, this is a per-project effort! Even if I’ve used a library in one project, I might not want to use it in my other projects.

Of course, this just scratches the surface of the many interesting and exciting features of Cabal. There are many others -- for example, the token that begins comments begins comments as long as it’s the first non-space character in a line – but not otherwise!. I’ve been really inspired in my work with Cabal so far – I only hope that someday I can write something nearly as useful!

Wednesday, March 19, 2008

Two Quotes

from Gwen Stefani:

"Sometimes it's so hard to find what I'm trying to say. People may think you can turn creativity on and off, but it's not like that."

"This shit is bananas! B-A-N-A-N-A-S!"

Perhaps she should spend more time in the off position is all I am saying . . .

Tuesday, November 27, 2007

Competing Products are Competition

There's been some noise on programming.reddit.com recently about a Wall Street Journal story describing how Intel and Microsoft are apparently crushing the OLPC laptop.  According to the WSJ article, Microsoft and Intel have been working together to sell low-priced laptops running standard Windows technologies to developing countries at prices that make them competitive with Mr. Negroponte's OLPC laptop initiative.

The article quotes Mr. Negroponte as claiming that Intel was attempting to "undermine" his initiative, and saying that he didn't want to compete in "bake-offs" with Intel's machine.  I'm a bit confused by this.  While it's all well and good that Mr. Negroponte's company is non-profit, it doesn't magically mean that their products don't compete with Intel's.  In fact, if you read the description of the machine, which is based on AMD processors and an operating system using the Linux kernel, it would seem to be designed precisely to avoid Intel and Microsoft's dominance in the US market.  It seems no more remarkable that Intel and Microsoft consider this competition as that IBM's competitors disliked their charitably giving computers to all the major engineering schools in the 50s and 60s.

Perhaps most humorous is Mr. Negroponte's assertion, early in the article, that his "goal is not selling laptops" and that he's in the education business.  In that case, it's hard to see why he should be upset that Intel and Microsoft are aiming products at the same market he's reaching with the OLPC; this is only achieving his eventual goal of allowing children in developing countries access to first-world technology.  He claims he's good at selling ideas, not laptops; it seems from here that his ideas have sold.

Wednesday, November 21, 2007

Roger Ebert Is Dumb

There's apparently been something of an ongoing spat between Roger Ebert and various people who write about games as to whether games are art. Since Roger Ebert's connections to the games industry are at best tenuous, and from all the evidence I've seen he's never bothered to play one, this seems a bit like his staking out a claim that the Mona Lisa isn't really art.

But I suppose that's neither here nor there. In his review of the Hitman movie, based on the games of the same title, he writes:

Other scenes, which involve Agent 47 striding down corridors, an automatic weapon in each hand, shooting down opponents who come dressed as Jedi troopers in black. These scenes are no doubt from the video game. The troopers spring into sight, pop up and start shooting, and he has target practice. He also jumps out of windows without knowing where he's going to land, and that feels like he's cashing in a chip he won earlier in the game.
This is particularly funny since, as a fan of the Hitman games, those are the scenes that made me least interested in the movie. Hitman is about disguise and stealth; striding down halls shooting bad guys is possible, but the game discourages it. As for cashing in chips he won earlier in the game, that sentence is a great mystery to me.

The sad thing about the whole fiasco is that most games aren't art, and the approach most game designers are taking isn't going to lead to art. Whether that's the fault of the designers or the publishers, how games fit into the current ideas of art, and what the artistic potential of games is and ought to be are all valid and interesting questions. However, Ebert is doing no one any good by trying to be part of this discussion when he clearly can't even be bothered to do basic research on what he says.

Saturday, September 08, 2007

Ed Haldeman is a fucking liar

which I suppose I should qualify by saying that I have relatively little knowledge of his personal life, so my adjective usage is speculative.

Recently, the Dartmouth board of trustees has found itself in the unusual position of having to actually live with some of the platitudes they have been mouthing presumably since time immemorial (or at least, since the until today current makeup of the board was established in 1891).  Dartmouth's board has, since 1891, had half of its members elected by the alumni.   This allowed Dartmouth's alumni to feel like they had a more significant role in the future of the college than they would have in most similar institutions, but until recently has not fundamentally changed the way the board of trustees functions.  The board is a tightly held, monolithic, self-sustaining organization, and it always has been.  The alumni-elected members of the board were selected and nominated by the College, rubber stamped by the alumni, and for all intents and purposes might as well have been chosen by the board directly without all the extra paperwork.

All this changed in 2004, when T. J. Rodgers won a petition campaign to be nominated by the alumni, and was subsequently elected to the board of trustees.  Rodgers had been public about his disagreement with the direction of the College during his election campaign, and his election represented a significant break with the monolithic makeup of the Dartmouth board.  Following his election, the trustee elections have all gone to petition candidates, all of whom have been opposed to the College's current policies and directions.

Today, the board of trustees finally got around to fixing this ongoing alumni uprising.  The size of the board of trustees was increased by eight non-elected members, guaranteeing that the non-elected members of the board can maintain a permanent majority over any upstarts the alumni choose to elect.  The chairman of the board, Ed Haldeman, sent a letter to the alumni today detailing the reasons for the board's decision.  Among the choice parts:

The changes we are making preserve alumni democracy at Dartmouth by keeping eight alumni-nominated trustees. They expand the Board with eight additional charter trustees, adding alumni to meet the needs of the College. And, they address the destructive politicization of trustee campaigns that have hurt Dartmouth. These changes represent a balancing of competing interests.

The changes the board made do precisely the opposite of preserving alumni democracy.  They ensure that the alumni cannot even elect enough board members to stall the board's decision, let alone create a majority.  Mr. Haldeman throws in extra fluff about the new non-elected members being alumni - but all of the board (excluding the ex officio members) are alumni, and were when the alumni elected Mr. Rodgers.  Finally, this is the first of many references to the "destructive politicization" of Dartmouth board elections.  It's safe to assume that "destructive politicization" is code for "debate," in much the same way that "partisan stalling tactics" is generally code for "disagreement."

And, they will ensure that, moving forward, the College has a strong, effective, and independent governing body.

It is worth wondering from what the board is maintaining its independence.  in this case, probably the alumni, as well as other annoyances like accountability.

Mr. Haldeman further explains how the newly expanded board will "Better Meet the Needs of the College:"

We also are giving the Board more flexibility to select trustees who offer the specific talents and experiences that the College needs, which elections don't ensure.

The recent elections have selected such complete incompetents as the founder and CEO of a major semi-conductor company, and two professors of law.  These individuals cannot be expected to have the wide range of skills it takes to manage a small college, of course.

Mr Haldeman expands on his earlier point about maintaining alumni democracy:

We are maintaining alumni trustee elections at their current level and preserving the ability of alumni to petition onto the ballot.

This is a blatant lie.  The percentage of alumni representation among the (non ex officio) trustees has dropped from 50 to 33 percent.  Whether this was accomplished by increasing the number of non-elected trustees, decreasing the number of elected trustees, increasing both but unequally, or sharks with lasers is trivia, and I'm sure that Mr. Haldeman's high quality Dartmouth education allowed him to see that.

A larger group of trustees representing even more diverse backgrounds will help us enhance Board engagement with key areas of the College including academic affairs, student life, and alumni relations. We are therefore creating new Board committees focused on each of these three critical areas.  This will facilitate greater interaction and communication with individuals in each of these three areas.

This is a bold new take on democracy: rather than allowing the represented to choose their representatives, the government will choose representatives.  This is necessary because the represented, with their high-quality highly priced Dartmouth educations, are too stupid to choose trustees who will actually represent their interests and goals.

Mr. Haldeman can't do without another comment or two about divisiveness:

I know there are strongly held views on all sides of this issue. And I respect that many of those views are driven above all by a desire to do what is best for Dartmouth and its students. But some of the recent rhetoric in this debate has become so harsh and divisive it is now doing harm to Dartmouth.

But it's worth mentioning that nowhere has Mr. Haldeman mentioned how the "politicization" or "divisiveness" or "partisan gerrymandering" or "counter-revolutionary behavior" that he's so eager to stamp out is actually harming the College.  In fact, despite having 1254 words of body text in his email to the alumni, repeating the same tired rhetoric in many cases two or three times, he cannot point to a single harm to the College or its members.  The only harm being done is to Mr. Haldeman's personal ability to run his little fiefdom without interference.

Mr. Haldeman closes with:

And, I look forward to continuing to work with all of you - alumni, faculty, students and parents - to build on Dartmouth's unique and pre-eminent place in American higher education.

I believe that he has probably convinced himself, over the course of this debate, that he does want to work with the faculty, parents, and alumni to improve things at Dartmouth, in much the same way that I believe that President Bush has convinced himself at each stage of our failure in Iraq that he is doing what's best for both the United States and Iraqi population, or that Stalin convinced himself that starving 6 million Ukrainians was a necessary step to establishing a happier future for everyone, including the Ukraine. 

Unfortunately for Mr. Haldeman, he is wrong.  The steps he and the board have taken are not increasing involvement with the alumni, nor are they addressing the issues that first led to the increase alumni politicization.  As with Stalin and the Ukraine, Mr. Haldeman is showing all of us alumni who's boss, and on who's terms we're going to play.

From that angle, he's been successful.  Unless the expected flurry of lawsuits reverses the Board's decisions, Mr. Haldeman presumably will find that there's less public debate, for much the same reason that elections in the USSR were simpler, quieter things than they ever were in this country.

However, I hope Mr. Haldeman discovers alumni reacting in a different way.  Dartmouth's split board provided an interesting mode of expression to alumni not available to them at many schools; it also made it unnecessary for alumni to vote with their contributions.  However, this is no longer the case.  For alumni disinterested in the College's policies, the only remaining option is to stop contributing.

I sincerely hope they do, and I pledge personally to not contribute to Dartmouth until the board parity is restored and Mr. Haldeman steps down as president of the board (and hopefully, retires from the board altogether).  What he has done is reprehensible.  He has changed one of the fundamental principles of the board.  He has lied to the alumni about his motivations and that of the board, and about the effects of his change.  This is not acceptable conduct from someone in any position of responsibility, from the President of the United States to the Chairman of the Board of a small liberal arts college.

- J. Garrett Morris, D'05.

Some post scripts:

- It is certainly true that the decision made by the board had many contributors, not simply Mr. Haldeman.  However, Mr. Haldeman chose to put his name, alone, at the bottom of the explanation to the alumni, so I see no reason that he should not be held responsible for the content, and motivation, of that explanation and the actions it explains.

- It is also true that I do not agree with the policies of the board, and do agree with the statements of the various petition candidates.  However, the board's decisions go far beyond the current issues facing the school to fundamentally undermining alumni input.

- Finally, it is unquestionably true that should alumni stop donating to the College, it may damage the quality of education or student life available at Dartmouth.  This is an inevitable result of the decisions of the board, and hopefully one that will be reversed when the board realizes the fundamental errors in their decisions.  It is also true that Dartmouth educates somewhere under 2000 undergraduates per year.  These students are highly motivated, gifted individuals who have many options open to them, and would obtain a quality education many places besides Dartmouth.  In that sense, were Dartmouth to fall into the Connecticut River tomorrow, the quality of education in America would not be significantly affected.  Rather, Dartmouth offers a specific environment to its students, and the damage to that environment is already being done - or has been done - by the administration and the board.  Any effect of a drop in alumni contributions will simply make that effect visible to those unwilling or unable to see it beforehand.

Saturday, May 12, 2007

Purest Idiocy

It appears that once again, various people are discovering that writing webapps in Ruby with Rails requires fewer keystrokes than writing similar things in Java with Struts and Springs.  While this is hardly a new realization (and is, one could mention, the entire justification for the existance of Rails), they then go on to conclude that the real culprit here is static typing.

I address this before when a different set of pundits came to the same conclusion (you know, roughly two years ago now), but this iteration's dynamic-typing proponents have come up with a couple of bold new ideas:

If you have pervasive testing, static typing == more typing. The static typing is nothing but a requirement to type extraneous code to satisfy a compiler that isn't telling you anything interesting anymore.

So, let's play the Hello, World game:

Ruby (dynamically typed): puts "Hello, World"

Haskell (statically typed): main = putStrLn "Hello, World"

Wow, look at all that extra typing.

The part that's particularly confusing to me, though, is the part where he concludes that if you already have pervasive testing, you don't need static type checking.  The goal of static typing is to describe (and thus constrain) the behavior of your code, and thus reduce the number of cases in which failure can occur (and thus the number of things that need to be, incompletely I might add, checked with test cases).

It's not a simple trade-off, though.  Consider a dynamically typed function:

(define (square x) (* x x))

compared to a statically typed:

square x = x * x

These both operate over the same domains, but in the case of the former, there's no static guarantee that the incoming values are within that domain.  (square "this") or (square :that) compile just a nicely as (square 2).  This means that to have complete testing coverage, you need to verify the behavior of square for all possible values in the language, not a constrained set.

Of course, things get more interesting once you get outside the world of printing hello world and squaring numbers.  Recently, I've had some fun discovering which parts of the .Net libraries are threadsafe.  While reading the documentation would have helped my quest, and testing did discover the non-threadsafe functions I'd missed (granted, four hours into batch runs), in Haskell I'd have been able to tell from the types whether or not the operations were threadsafe.  That's a stronger assertion than I've seen any testing library be able to make.

Thursday, February 22, 2007

Concentric Octagons

or, Sweet Jesus Why!?, being functional anti-pattern the seventh.

This anti-pattern will be presented in the form of a problem, with progressively better solutions until I arrive at a rough approximation of the original code.

The problem

You are provided with a source figure and a destination figure; both of these are arbitrary polygons. Your goal is to return the heading from the center of mass of the source polygon to the center of mass of the destination polygon.

A polygon is represented as a counter-clockwise list of points. The polygon is not guaranteed to be convex. To make your life easier, a function

centerOfMass :: Polygon -> Point

is provided. The centerOfMass function does not return (or not). You also have a function

polygonIntersections :: Polygon -> Polygon -> [Point]

which finds the (possibly empty) set of points at which two polygons intersect.

The First Solution

heading source dest = atan2 (y' - y) (x' - x)
where (x,y) = centerOfMass source
(x',y') = centerOfMass dest

Unfortunately, this solution is too simple and takes too few lines to be seriously considered.


The Second Solution


From high school geometry, you may remember that the segment between the two points of intersection of two circles is perpendicular to the segment between their centers. We can take advantage of this to find the segment between their centers: first, we can use the centerOfMass function to find the centers of the polygons and then compute the distance r between the centers. Then, we can construct two circles, concentric with the polygons and each having radius r. Finally, we can find the points of intersection, compute the slope of the segment between them, compute the heading based on that slope, and then rotate it 90 degrees (since we want the heading from center to center instead of from intersection point to intersection point).


Unfortunately, there are several difficulties with this code. First, our library of routines does not include routines for manipulating circles, and while it may seem somewhat appealing to represent a circle as an infinite list of points, manipulating that list is likely to be difficult. A second difficulty is that the circles used are larger than they need to be. We will address both these problems in the next solutions.


The Third Solution


First, we will address the problem with representing circles. As I mentioned, we do not have either a representation for circles, or routines manipulating them. However, we do have code to manipulate polygons, so we can appoximate them. Obviously, the more sides a polygon has, the better approximation it will provide; for our purposes, eight should be fine. While this will introduce some deviation in the result, it will surely not be too much. We now have the function


octagonFromCenterAndRadius :: Point -> Double -> [Point]


whose implementation we leave to the reader.


Sadly, we have not yet found a use for infinite lists. Luckily, we have one more problem to solve.


The Fourth Solution


As previously mentioned, our solutions so far have relied on computing the distance between the centers of mass of the two input polygons, and creating circles (or octagons) of that size. However, this creates shapes that are larger than we precisely need. Instead, we can take advantages of infinite lists and filtering to ease this problem:


octagonsFromCenter c = map f [1..]
where f = octagonFromCenterAndRadius c


Now, we are better equipped to find the intersection points to use in the process from Solution 2:

intersectionPoints p1 p2 =
take 2 $ head $ filter (not . null) points
where c1 = centerOfMass p1
c2 = centerOfMass p2
points = zipWith
polygonIntersections
(octagonsFromCenter c1)
(octagonsFromCenter c2)

And, at this point, I believe we can be happy with our solution. Note that we no longer have to find the distance between the centers of mass, and have thus saved outself a square root operation.


A Confession


I have made up this derivation of the final solution. In fact, I was not around when the original solution was divised; I only discovered this algorithm while attempting to understand what happened in a module I was supposed to be porting to a more modern version of our libraries. However, none of the fundamental points of the algorithm have changed: the polygonIntersections and centerOfMass functions were both used in the original solution and they were used to generate infinite lists of octagons such that the intersection points of the first two that intersected could be used to find a heading between the centers of mass.


I suppose there are any number of lessons you could draw from this code. I've always seen it as an example of two things: first, that sometimes it's not worth looking for a more interesting solution, but that second, if you must find a more interesting solution, at least it will be amusing to future generations.

"No, it's purely functional"

or, I will have none of your State monad, being functional anti-pattern the sixth.

Shortly after arriving at my previous employer, I was talking with oen of the senior developers, and some piece of state passing code came up. Having recently discovered the exciting world of the Monad Transformer Library, I said, eagerly, "Oh, are you using the State monad for that?" To which he replied, "No, the code is all purely functional," and seemed quite proud of himself. At the time, I was quite confused. Later, I became more confused, when it became apparent that every function in his code started:

function :: AppEngine -> ... -> STM stuff
function engine ... =

Of course, he rarely actually wanted the AppEngine. Frequently he actually wanted the DataStore, which lived inside a GenericEngine, inside the AppEngine. So, each function contained the line

where dataStore = store (genericEngine engine)

By this time, readers will presumably have wondered what is done with an AppEngine or its contents, since they seem to go in but not come out. However, as luck would have it, the contents of AppEngines et. al. were all TVars, so they could be updated using writeTVar, without having to worry about polluting your code with icky, icky monads.

I was more perplexed. However, as annoying as passing the engine as the first parameter to everything was, it was at least fairly obvious, even without being too familiar with the code in question.

On the other hand, a later example of this behavior I ran across was somewhat harder to decode. In this case, I was translating a set of generic action descriptions into situation specific information. The meat of the code had been written elsewhere, and I was given some fairly simple tools: an ActionSegment data structure, and a function:

makeSpecific :: Point -> ActionSegment
-> ActionSegment
makeSpecific startingPt segment = ...

Lest this seem somewhat confusing, both generic and specific actions were represented as lists of action segments. The difference was that the generic ones had fields with relative values, and the makeSpecific function ferretted out those values and replaced them with non-relative variations. The starting point argument also confused me a bit, since each ActionSegment included a starting point already, in non-relative form. However, after scouting around a bit for a use of startingPt, I noticed that the Point type implemented the (or not) pattern, so I happily passed NoPoint and ran it to find out what happened. And, as far as I could tell, it worked wonderfully. Generic fields became specific, I could generate the information I needed from there, and so on.

As it happened, though, subtle bugs started appearing. I eventually traced them back to the startingPt field. It turned out that when I got a list of ActionSegments, only the first had a valid starting point field. The starting points of the remaining ActionSegments had to be manually initialized to the computing finishing points of the previous segments, using the argument to makeSpecific. (As it happened, it was also advisable to pull the valid starting point out of the first segment and pass it to makeSpecific, since said function would happily overwrite the valid value in the first segment.) To finish complicating things, ActionSegments could contain sub-segments, so to speak, which had similarly fascinating starting and ending point relationships with their parents.

Some time later, I was talking to the original developer, and mentioned casually having discovered that feature late one night. Oh, yeah, he explained, well, the finishing point was in the result already so he didn't want to waste memory by returning it again.