Yesterday, I talked to a person who uses mostly C++, trying to show him my current project in CL. It was really a mess – there are almost no current Debian- or ASDF-Packages, the whole stuff is full of bugs and hard to use for someone not familiar with it, and well, in the end we gave up. It was simply not possible to compile it on that computer without really having to put in huge efforts – only to show my alpha-code. Well, maybe I should try to begin using clbuild. Seems like clbuild could make that part easier.
Anyway, that person then noticed that one has to load Packages into „the compiler“, which is – to a person using C++ – unfamiliar. To me, this was interesting, because it was the same problem I had when beginning to use Common Lisp. There are no makefiles, and there is no really clean way of compiling. In fact, there is not even a real distinction between the program itself, the lisp-runtime and the compiler. When using SBCL, CLISP or ALISP, you will simply produce coredumps. You can put this production of executable coredumps inside some Makefile or Build-Script. But still, this is something different from compiling, at least intuitively. „Compiling“ is done almost every time when you define a function in the REPL or in some file. The compiled code is then stored somewhere in your heap. The coredump then contents all these functions, but in the end its just a dump of everything you have defined so far.
Well, as seen from C++ (and most other mainstream-languages), this isnt „clean“. For the ordinary programmer, there may be scripting engines which do not need any transformation, but in general, you have your code, which you can modify, then you have a clearly seperated process of compiling (maybe applying a few preprocessors before), and then afterwards the clearly seperated process of running the program. Compiling is seen as some kind of „cleanup“. You can put comments in your code, modify it, choose namings for variables and functions, transform it, but as soon as you compile, most of this is dropped and only a clean blob of machine code remains, where the place of every byte is – even though by too many definitions to keep track for a human – clearly determined. That is, running the same compiler on different systems should (mostly) produce the same code.
When coredumping with Common Lisp, you cant say much of where the bytes are put. In theory, a small changing of the code in some macro which does not even affect the return-value of this macro, and thus does not change the code of the functions that are run in the program you create in any way, could lead to an additional garbage-collection-cycle, which could dramatically change the contents of the core-image, even though the program is supposed to do the exactly same thing. Of course, in general this doesnt happen, I am not even sure if there is can happen in any CL implementation anyway. But there is no guarantee, and most of the lisp-programmers wouldnt care. Of course, there could be C++-Compilers which do something similar. But thats unusual, and well, C++-Programmers would most probably care.
The main difference here lies – in my oppinnion – in the programmer’s interpretation of the process of compilation. For a C++-Programmer, compilation is an intrinsic part of programming. Maybe its some kind of ritual, maybe its some possibility of gaining more free time, maybe its for technical reasons, but its something that comes with programming. For the CL-Programmer (at least for me and some others I know), compiling is just a process of optimization. There is no difference between interpreting a script-file and compiling it and then executing it, except that a compiled binary is usually faster, but is usually not supposed to be changed afterwards. Most implementations of CL have various optimization settings between just interpreting and compiling, i.e. converting into some faster interpretable bytecode, compiling only parts of the code, but also shift things that are clear, asserting to the compiler that you dont do some things.
Well, ok, one could say that these are different points of view, a different understanding of how programming should work, different directions the researchers took, one going more into the direction of Smalltalk, the other one going into the direction of Cobol or whatever. Actually, this might be true for a lot of other facets, but in that case, I think the major misunderstanding lies in the difference itself. For a C++ Programmer, Compiling means nothing else than for a Lisp Programmer, namely transforming the code into something the machine understands better. But most C++ Programmers will know (and accept) Scripting Languages for some special purposes, i.e. using bash for simple scripting, embedding scripting engines like lua or rhino. Namely for parts where flexibility is more important than runtime speed. The only real difference in that case is that you actually use other languages for doing this. Of course, you could build a scripting engine for a C++-Like language, but C++ is complicated, takes long to compile, and has too many concepts that do not fit into a scripting language. While bash has too much concepts that would not fit into a compiled language. In contrast, Common Lisp can do both, which brings a lot of advantages, but – of course – also drawbacks. You can produce code that is very efficient with Common Lisp, but lowlevel-stuff is – even though it is indeed possible to do – a lot harder than in C++. And of course, in your coredump, you will have a lot of boilerplate-stuff you will never need. In CLISP, a binary takes about 4+x Megabytes, while in SBCL, you wont get binaries under 24 Megabytes. For huge projects this is no problem (in times when a simple feedreader is 50 Megabytes huge). But for a little shell-tool, this is way too much. On the other hand, CL is not made to create little shell-tools. For Common Lisp, the REPL is what the Shell is for the C++-Programmer. In the REPL, you can define a small function, which you can simply run there and which doesnt really take away much place. While a C++-Programmer uses a lot of distinct processes of its OS to produce his software, a CL-Programmer needs mostly its CL-Runtime, which then runs everything else he needs. CL is more like an operating system in that sense.
Still, both kinds of programmers actually do the same thing and actually have the same oppinion about. Compilation is something you do to produce efficient code.