Why is X not written in Lisp?
or
C, man, C!

Juliusz Chroboczek

11 February 1998

Chapter 1. Ground values.

Consider the following code:

  (setf (font-properties xf) '(:fontname "FOO" :pointsize 5))

A first attempt at a translation into X idioms gives:

  xf->info.nprops=2;
  xf->info.props=xalloc(xf->info.nprops*sizeof(FontPropRec));
  xf->info.props[0].name="FONTNAME";
  xf->info.props[0].value="FOO";
  xf->info.props[1].name="POINTSIZE";
  xf->info.props[1].value=5;

You'll notice the convenient syntax and flexible representation. But as we don't have exceptions (signals) in the language, we need to check every return value pedibus calcantibus:

  xf->info.nprops=2;
  if((xf->info.props=xalloc(xf->info.nprops*sizeof(FontPropRec)))==0)
    return BadAlloc;
  xf->info.props[0].name="FONTNAME";
  xf->info.props[0].value="FOO";
  xf->info.props[1].name="POINTSIZE";
  xf->info.props[1].value=5;

You'll notice the elegant manner of adding an error value to a data type by overloading 0. As we have no dynamic typing or usable sum types (did I hear `union'? are you kidding?), we intern the strings and use integer handles.

  xf->info.nprops=2;
  if((xf->info.props=xalloc(xf->info.nprops*sizeof(FontPropRec)))==0)
    return BadAlloc;
  xf->info.props[0].name=MakeAtom("FONTNAME");
  xf->info.props[0].value=MakeAtom("FOO");
  xf->info.props[1].name=MakeAtom("POINTSIZE");
  xf->info.props[1].value=5;

We are now unable to distinguish between string handles and bona fide integers. We therefore introduce another array.

  xf->info.nprops=2;
  if((xf->info.props=xalloc(xf->info.nprops*sizeof(FontPropRec)))==0)
    return BadAlloc;
  if((xf->info.isStringProp=xalloc(2))==0)
    return BadAlloc;
  xf->info.props[0].name=MakeAtom("FONTNAME");
  xf->info.props[0].value=MakeAtom("FOO");
  xf->info.isStringProp[0]=1;
  xf->info.props[1].name=MakeAtom("POINTSIZE");
  xf->info.props[1].value=5;
  xf->info.isStringProp[1]=0;

Unfortunately, if the second allocation fails, there's a memory leak. We could store on a stack the values to release in case of failure, but that would involve more allocation, which would... We give up on this idea, and write the more C-like:

  xf->info.nprops=2;
  if((xf->info.props=xalloc(xf->info.nprops*sizeof(FontPropRec)))==0)
    return BadAlloc;
  if((xf->info.isStringProp=xalloc(2))==0) {
    xfree(xf->info.props);
    return BadAlloc;
  }
  xf->info.props[0].name=MakeAtom("FONTNAME");
  xf->info.props[0].value=MakeAtom("FOO");
  xf->info.isStringProp[0]=1;
  xf->info.props[1].name=MakeAtom("POINTSIZE");
  xf->info.props[1].value=5;
  xf->info.isStringProp[1]=0;

Which, of course, means that if we add some code that allocates data earlier on, we have to manually update all the emergency deallocations. Oh, well!

At this point, the reader is advised to consider the initial Lisp code again:

  (setf (font-properties xf) '(:fontname "FOO" :pointsize 5))
and notice how much more explicit and simple the C code is.

(Chapter 2, “Closures,” fails to get written as author collapses, hysterically sobbing, when thinking of the amount of manual lambda-lifting he had to do.)

Juliusz Chroboczek.