Subject: Re: low quality lisp books From: Erik Naggum <erik@naggum.net> Date: Wed, 19 Sep 2001 02:01:38 GMT Newsgroups: comp.lang.lisp Message-ID: <3209853697331820@naggum.net> * p-m@yahoo.com (Philip) > Lately I've been thinking of buying a lisp book. I was thinking of Paul > graham's book, but my friend pointed out that its not very good. He had a > problem with the guy lying on the first page. What do you guys think? That your friend is a stupid liar who missed the point entirely. A lexical closure is a function-callable structure with local data. You construct a new such structure by capturing the values of all variables bindings that close over the function. However, this structure can be called directly with language primitives and it looks no different than normal functions. (defun addn (n) (lambda (x) (+ x n))) amd you call this with (funcall (addn 1) 2) Now, there are several interesting problems with this solution from a C point of view. The first and foremost is that writing a function that can call an arbitrary function or function-like object in C with an arbitrary number of arguments is _really_ hard. Thefore, you typically write a specialized function in C. This is the first case of cheating. You see, the lexical closure support in Common Lisp is _general_. struct closure { int (*body) (int, struct closure *, ...); int args; ]; /* This would be done with classes and inheritance in C++. */ struct addn_closure { int (*body) (int, struct closure *, ...); int args; int n; } int addn_lambda (int count, struct closure *closure, va_list arg_list) { int argument; argument = va_arg (arg_list, int) return (argument + (struct addn_closure *)closure->n); } struct addn_closure * addn (int n) { /* This would be done with class initialization in C++. */ struct addn_closure *new_closure = malloc (sizeof (struct addn_closure)); new_closure->body = addn_lambda; new_closure->n = n; return new_closure; } int funcall_closure (int count, struct closure *closure, ...) { int result; va_list arg_list; /* This would be done with exceptions in C++. */ if (count != closure->args) exit (1); va_start (arg_list, closure); result +> closure->body (count, closure, arg_list); va_end (arg_list); return result; } Now we can do this, more or less, but you may have to engage in casting to get the stupid static typing cruft in the compiler to agree with you, which also screwed up our ability to use any other argument type than int, which is really, really painful. (So maybe it should have used pointers to various object types, and passed void* around instead, but then you could not have passed integer constants directly, but would have had to make up some objects to hold such values.) funcall_closure (1, addn (1), 2); If you think all of this supports your (friend's) view that Paul Graham is a liar and that you _can_ do lexical closures in C, you are actually gravely mistaken. We are cheating and limiting the functionality all over the place. More than half of what cannot be done in C is precisely the generality of the solution. It is perhaps possible to do lexical closures in C++, with support for overloading and lots and lots of heavy-duty features, but even with STL and all kinds of "foundation classes" and shit, it is not actually done. There has to be a good reason for this, considering how tremendously useful lexical closures are when passing functions around, and the reason is two-fold: (1) Since it is so incredibly hard to do this in C/C++, it is not done, and (2) since it is not done, the kinds of solutions that benefit from it are not done in C/C++. Therefore, a C/C++ programmer, like your friend, will have absolutely no use for lexical closures and neither will will he understand how he missed the point, because the whole concept is _actually_ outside the reach of the C/C++ mindset. I suggest you get smarter friends who do not keep you down by confusing you with negative bullshit when you clearly should spend your time reading and understanding books that open your mind instead of closing them down. You might then learn something and be able to tell your friend that he is a liar in terms he can understand, although it is highly doubtful that he will grow a clue considering this insistence that he need not understand what he argues against. This seems to happen to C/C++ people a lot, and it is part of the survival tactics of those language to keep people from discovering that their languages are limiting them to that which is easy to do in thelr languages. Common Lisp makes a lot of simple things in those languages a little harder, but the things that are _really_ hard in C/C++ is no harder than the rest, and when you run out of hard things to do in C/C++, the complex stuff you work with in Common Lisp is still going to be equally hard to what you have been used to. This is where higher-level languages beat lower-level languages. Incidentally, it is far easier to do lexical closures at the assembly level than at the C level. This is an indication that C/C++ is a step up from assembly, but in the wrong direction, such that you cannot get any further, and your friend hit the C level from _below_ (and lots of other C/C++ people react the same way), and cannot get anywhere. This is the _real_ tragedy of C/C++ and the people who think they do not need what these languages cannot provide (easily) are so pathetic it hurts to watch them make fools of themselves. Incidentally, there are not enough high-quality Common Lisp books that explain things to people who have been used to working with the machine at the C level. I may think a book like that would have worked better for you, too, but the only one I can think of is "Anatomy of Lisp", which is out of print and hard to come by. ///