jingtaozf / literate-lisp

Load Common Lisp code blocks from Org files
63 stars 7 forks source link

:eval-when header argument #9

Closed akater closed 4 years ago

akater commented 4 years ago

I suggest to provide :eval-when header argument as an additional feature, and maybe retire :load header argument altogether.

Example:

#+begin_src lisp :eval-when compile
forms*
#+end_src

should be read as

(eval-when (:compile-toplevel) forms*)

In general, :eval-when compile :eval-when compile load :eval-when load should wrap forms in the corresponding eval-when.

The default (absence of :eval-when) should likely correspond to :eval-when load.

There is no need to provide support for :execute here, due to semantics of :execute and the fact that :eval-when would not affect org-babel evaluation. However, it might be appropriate to blindly translate :eval-when whatever and-more into

(eval-when (:whatever :and-more) ..)

while treating compile and load as special cases.

When it comes to preventing evaluation, several approaches come to mind:

The first one comes from Emacs Lisp tradition (cf. :eval never), the second one—from Common Lisp.

I also believe that not ignoring the block in the presence of :eval never is a bug.

Using just :eval (and thus extending the existin org header) is OK too but I believe it would be better to separate those as they are meant for different functions, namely: eval and compile-file.

jingtaozf commented 4 years ago

header argument load is just an optional alternative to the original common lisp feature, It is provided so we can insert codes for different purpose, but you can still use original common lisp feature by the dispatcher #+ or #-.

A programmer can use eval-when in a code block directly, so I don't think it's a necessary feature for literate programming,unless it can give us some additional benefit.

akater commented 4 years ago

Jingtao Xu notifications@github.com writes:

header argument load is just an optional alternative to the original common lisp feature, It is provided so we can insert codes for different purpose, but you can still use original common lisp feature by the dispatcher #+.

I didn't study the implementation, sorry. What feature is it an alternative to? What is this original feature do you have in mind that could go to #+?

A programmer can use eval-when in a code block directly, so I don't think it's a necessary feature for literate programming,unless it can give use additional benefit.

The primary benefit is interface consistency. You implemented a reader macro. All such introduce special syntax. Your syntax effectively allows to conditionally evaluate parts of source code, or ignore them. (Or maybe, to read or not to read them; more on this later.) I believe it would be better if this syntax was consistent with the existing syntax that allows to conditionally evaluate parts of code, or ignore them. In Common Lisp, this syntax is eval-when. I suggest to reuse the existing name and its feature set which is a superset of the existing one—which is not surprising: eval-when serves the same purpose but it had absorbed decades of experience. You think nobody would ask for :compile yes in addition to :load no? I believe it is totally natural to want one when you already have the other.

One additional benefit is convenience in changing evaluation mode of blocks of code (as opposed to individual forms): I could imagine elisp function wrap-defun-in-eval-when but selecting multiple top-level forms to wrap would be far less convenient while in org mode they are already seleched by the block. In contrast, I can easily imagine :eval-when switcher for block at point that works like dired-do-chmod—which is both much more convenient and more straightforward to implement than (un)wrapping whole forms (or, worse, collections of forms) in full-blown eval-when expressions. E.g. ‹key for ob-lisp-do-cheval› ce RET could change the header to :eval-when compile execute.

Another additional benefit is, less noisy diffs.

I can see how I can be wrong about the following detail: depending on how you implemented a reader macro, there might be a slight difference: with traditional eval-when, the form is always read; with header argument :eval-when form(s) might not even be read. It thus would be more precise to call it :read-when, then. But I'm not certain on this; :eval-when seems fine to me even if it actually means read-when, unless we have to deal with tangling (more on this below).

To share some personal experience, my use cases for :load no are blocks that contain examples and, more generally, tests. I never use :load yes as I never tried to override this default value. :load no is used both in examples to be evaluated interactively, and in examples to not be evaluated interactively, in which latter case it is coupled with :eval never.

With support for :eval-when and some non-intrusive patches to Org,

Last but not least, I think it's better to be able to use one single parameter rather than two different parameters :eval and :load that have the same scope, are thus interdependent and have thus to be re-adjusted in concert. The ability to write :eval-when compile would be a very nice consequence. Tangling is of no interest to me (why would it be of intrest to any user of literate-lisp?) but objectively, tangling is an operation on a file, on par with loading a file and compiling a file. It thus makes total sense to be able to write :eval-when tangle for code to be evaluated when tangling the file. Traditional :tangle yes would then however certainly correspond to :read-when tangle, so here we must make this distinction I discussed above.

What are your usage patterns for :load no and :load yes? What we're doing is quite experimental so different people could discover quite different practices.

akater commented 4 years ago

Regarding your comment that eval-when is accessible to programmers anyway—well, let form is also accessible to programmers, and yet Org does provide :var header argument. Looks like it is not supported by Lisp backend but that's the issue with the backend.

jingtaozf commented 4 years ago

The next interesting feature of literate-lisp is the web syntax, so we can organize codes as blocks, instead of just one, for example this: https://github.com/jingtaozf/literate-lisp/blob/web/literate-lisp.org#web-syntax

I agreed that different people could discover quite different practices and we can use one header argument instead of two here.

A patch with good design is welcome.

jingtaozf commented 4 years ago

Please re-open this issue if you still have questions about it.