Common Lisp: compilation vs evaluation

2019-04-08 10:52发布

问题:

On Emacs + Slime with sbcl, once I define a function (or more) in a file I have two choices:

  • Evaluation: e.g. with C-M-x eval-defun
  • Compilation: e.g. with C-c M-k compile-file

The second one produces a .fasl file, too.

What are the differences between the two?

What's going on under the hood when I compile a definition / a file?

What are the Pros and Cons of each one?

回答1:

First of all, there's a function eval[1], that allows to evaluate (i.e. execute) arbitrary CL form in the language runtime. CL implementations may have 2 different modes of operation: compilation mode and interpretation mode. Compilation mode implies, that before evaluation the form is first compiled in-memory. Also in CL evaluation happens not on the file-level, but on the level of individual forms. So eval may both compile and interpret the form, depending on the mode of operation. (For example SBCL by default always compiles, unless you instruct it not to by setting sb-ext:*evaluator-mode* to :interpret, while CLISP always interprets).

Now, there's also a convenience function compile-file[2] that allows to compile all the forms in some file and save the results in another file. This doesn't trigger evaluation of these forms.

Also CL defines 3 distinct times of program lifecycle: compile-time, load-time and execution time. And there's a possibility to control what happens when with one of the most (if not the most) cryptic CL special operators eval-when[3].

To sum up, C-M-x eval-defun will call eval on the form under cursor. It will not necessary compile it, but that is possible, depending on implementation. C-c M-k compile-file will compile-file your buffer, but not evaluate it's contents.



回答2:

Maybe a metaphor will be a bit easier to understand.

Imagine that you have some job to do and there's a worker who can do it. Unfortunately, this worker doesn't know your language. Let's say you speak English, and he knows only French. So you need translator. Ok, no problem, you have translator too. Here you have 2 options:

  1. Stay near the worker, tell to translator what to do and see how worker does it.
  2. Ask translator to write the task down to the paper and then give this paper to worker each time you need the job to be executed.

If you need the job done only once, there's no big difference what way to go. However, if you want the same thing to be done many times and possibly by different workers (all French), you may want to get the paper with translated instructions.

So now to programming. You write the program in one language (e.g. Common Lisp), but computer itself doesn't understand it. It "speaks" only its internal language - native code. So you need some kind of translator. And that's where compiler comes into the game. Compiler translates (compiles) your code into native code so computer could execute it.

Just as in example with French worker, you have 2 options:

  1. Tell the computer what to do just when you need it. In this case compiler will translate instructions, computer will execute them, and both will forget about it immediately. This is called evaluation.
  2. Write instructions to the file and then use it to tell the computer what to do every time you need it. This is usually referred as compilation.

Note the mess in the terminology: actually, compiler works in both cases, but when you compare evaluation and compilation, the later refers to the 2nd case only. In other contexts terminology may differ, so try to understand underlying processes while reading about things like evaluation, compilation, interpretation and about translation in general.

Also note, that in SBCL REPL compilation (writing to the file) has a side effect of evaluation. So in this specific case the only difference is in writing to the file.



回答3:

What actually happens when you evaluate an expression is that it is sent to sbcl where the text of your expression will have to be parsed and then compiled into native code which will be stored in memory in the Common Lisp environment.

The second method will do the same but compile all the code into the file. The reason you would want to compile code is to make it quicker to load; there's no need to parse the syntax and semantics of your code and generate the code again, it can simply be loaded into memory ready to run.

So the benefits of compilation are just speed of loading, saving the computer work.

In the case of editing a file that is already compiled though, you can eval-defun to recreate a function in memory only, which may be quicker than compiling the whole file.



回答4:

This is not answering your question directly, but this is too long for the comment too.

Most of the times you wouldn't like to use either option, if we speak about SLIME and Emacs - you'd be using C-c C-c, (or M-x slime-compile-defun). This will pop up (if not already open) the compilation buffer, which shows compilation errors and warnings + it will highlight the problems inside your code. This also works nice with things like Flymake cursor (once you navigate to the problematic area it will show in the minibuffer what exactly the problem was).

Compiling a file happens in a rare event when you actually have a product you want to use later as it is. Or, most probably, you want others to use it w/o you having to set it up. For example, if you have a web server, and you want the system administrator to be able to (re)start it as needed - the administrator needs not to know how your software functions, she'd need only to know how to launch it.

Eval'ing a defun is just that - it sends the text to SWANK, but doesn't analyze the result. Of course your Lisp will print something back to you after you did that, but SLIME will stand aside.