[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [BUG] Inconsistent global/local :var assignments in ob-* for lisps a

From: Zelphir Kaltstahl
Subject: Re: [BUG] Inconsistent global/local :var assignments in ob-* for lisps and non-lisps (was: org-babel guile source block bug in handling multiple values)
Date: Fri, 10 Mar 2023 10:39:45 +0000

On 3/9/23 14:04, Ihor Radchenko wrote:
Zelphir Kaltstahl <zelphirkaltstahl@posteo.de> writes:

I am not sure (let ...) is a correct wrapper for noweb included source blocks.
What, if I write a (define ...) in my source block and want to use that source
block via noweb in another source block? Expected behavior I think would be to
be able to access those variables in other source blocks, since they are defined
on a top level in an earlier source block, but if they are wrapped in a (let
...), that would make them only available in the (let ...)? It seems to me, that
the simple wrapping with a (let ...) might not be the right thing to do. Testing

#+name: scheme-defs
#+begin_src scheme :eval query-export :noweb strip-export :session myguile 
:results output replace drawer :var x=1 :var y=2
(define a x)
(define b y)

#+name: scheme-time
#+begin_src scheme :eval query-export :noweb strip-export :session myguile 
:results output replace drawer
(simple-format #t "~a ~a\n" a b)

Indeed, that also does not work.
I just checked ob-C, ob-shell, ob-emacs-lisp, and ob-clojure.
Non-lisps appear to assign the values globally.
In contrast, all the lisp babel backends are using let-bindings.

Considering the existing inconsistency, and the raised bug I'd be in
favor of making variable assignments global in all the lisp babel

The only possible exception is ob-emacs-lisp. Executing elisp code is
done in current Elisp session and thus using global variable assignments
may be tricky. Unless we juggle with multiple obarrays.

I guess I did never hit this problem earlier, because I "oursourced" my imports
and in imports I do not need any :var header arguments.

I've asked on the Guile IRC channel and something interesting is the case here
(thanks for clearing it up flatwhatson!) and I understand it as follows:

Imports inside (let ...) work. It is just that let-values is a macro and macros
are expanded before execution time. However, Guile gets to the body of the
wrapping (let ...) at execution time. That means, that when Guile gets to
evaluate the body of the let, it does not expand the let-values, because it is
already at execution time and no longer at macro expansion time. The import
might import the let-values form, or might not, but it is already too late to
expand the (let-values ...).
So, apparently using `let' is not universally safe in Guile.
Is it safe in any Scheme? I think that depends on expansion and evaluation phases of the Scheme standards (see below).
OK, the question is though, whether org should wrap anything in a (let ...) at
all. During discussion on the Guile IRC, some points against let-wrapping were
brought up:

(1) The presence of a :var header argument currently determines, whether the
code in the source block is wrapped with a (let ...). One argument for that was,
that this way the variables do not leak. But this also decides, whether other
things leak. For example (import ...) or (define ...). Should :var decide,
whether bindings created with (define ...) are visible in other source blocks
including the source block with the :var header arguments? It seems like a
responsibility :var should not have and definitely is unexpected for the user.
This is something Guile-specific. In Elisp, let-binding still allows
`defun' or `defvar'.

The issue is not with defining via (define ...) inside a (let ...) in Guile. It is about importing macros at the time, when the body of the (let ...) is already evaluated, which is at a later phase than macro expansion. By wrapping inside a (let ...) org has moved the import to a later phase, which causes the macro (let-values ...) to not be expanded.

As far as I know, (defun ...) and (defvar ...) are merely defining functions and variables, not macros.

My point is, that imports are usually global for sessions. But :var decided for let-wrapping, moving them to a different place. Just like imports are usually global, I would expect (define ...)s to be global in the session, unless I put them inside a narrowed scope like a (let ...) myself. The org generated (let ...) is invisible to the user and thus confusing, at least for GNU Guile.

For other Schemes it probably all depends on how their phases of expansion and evaluation work. I don't know enough about the Scheme standards, to tell, whether Guile has the correct behavior here or whether there is a correct behavior defined in the Scheme standards. Maybe someone more knowledgeable can chime in to comment on that.

repositories: https://notabug.org/ZelphirKaltstahl

reply via email to

[Prev in Thread] Current Thread [Next in Thread]