emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Zelphir Kaltstahl <zelphirkaltstahl@posteo.de>
To: Bruno Barbier <brubar.cs@gmail.com>
Cc: emacs-orgmode@gnu.org, Ihor Radchenko <yantar92@posteo.net>
Subject: Re: org-babel guile source block bug in handling multiple values
Date: Thu,  9 Mar 2023 00:44:13 +0000	[thread overview]
Message-ID: <c6a7b45c-cb80-8102-ac21-8ac8592c71a7@posteo.de> (raw)
In-Reply-To: <6408e424.5d0a0220.8862a.2a62@mx.google.com>

[-- Attachment #1: Type: text/plain, Size: 8190 bytes --]

On 3/8/23 20:38, Bruno Barbier wrote:
> Hi Zelphir,
>
> Zelphir Kaltstahl<zelphirkaltstahl@posteo.de>  writes:
>
>> On 3/7/23 20:52, Bruno Barbier wrote:
>> Also thanks for the idea with sessions + separate import source block. I thought
>> that should work, but apparently that also has the same error, when running for
>> the first time:
>>
>> ...
> Oh, I see. I tested something way simpler :-)
>
> First, one block to open and configure the guile session.
>
>       #+begin_src scheme :session "!guile" :results silent
>       (import (except (rnrs base) error vector-map)
>                (only (guile)
>                      lambda*
>                      λ)
>                ;; let-values
>                (srfi srfi-11))
>       #+end_src
>
> Then, you can get to work and evaluate as many blocks as you like in
> that session:
>
>       #+begin_src scheme :session "!guile" :results output replace drawer :var x=1 :var y=2
>       (let-values ([(a b) (values x y)])
>          (simple-format #t "~a ~a\n" a b))
>       #+end_src
>
>       #+RESULTS:
>       :results:
>       1 2
>       :end:
>
> Bruno
Hello Bruno and hello mailing list!

I just tested it a little more:

If there is any (import ...) at all in the code block that makes use of the 
(let-values ...), it seems to somehow disturb already imported libraries. For 
example the following also does not work:

~~~~START~~~~
#+name: scheme-time-imports
#+begin_src scheme :eval query-export :noweb strip-export :session scheme-time :results output replace drawer"
(import
  (except (rnrs base) error vector-map)
  (only (guile)
        lambda*
        λ)
  ;; let-values
  (srfi srfi-11))
#+end_src

#+RESULTS: scheme-time-imports

#+name: scheme-time
#+begin_src scheme :eval query-export :noweb strip-export :session scheme-time :results output replace drawer :var x=1 :var y=2
<<scheme-time-imports>>
(import (srfi srfi-1))
(let-values ([(a b) (values x y)])
    (simple-format #t "~a ~a\n" a b))
#+end_src
~~~~~END~~~~~

So that means, even if the import has nothing to do with the actual import which 
would provide the let-values form, it disturbs it.

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 
that:

~~~~START~~~~
#+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)
#+end_src

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

Indeed, that also does not work.

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 ...).

What is still a bit weird about it is, that in the original example with 
`let-values` I don't get an error about `let-values` not being defined, but only 
about `a` not being defined. And in the example with (define ...) and :var 
above, I get a message about `x` not being defined, instead of `a` not being 
defined.

Probably a good general workaround is to only have imports at the top level, by 
moving them into a source block, which does not have any :var header arguments.

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.

(2) Wrapping in a (let ...) also moves things from the top level to the inside 
of expressions. This can make them move from one phase to another. Phases such 
as described here: 
https://www.gnu.org/software/guile/manual/html_node/Eval-When.html. This is 
probably not intended and also unexpected.

(3) Not wrapping with a (let ...) expression and instead going with simple 
(define ...) for :var variables seems a very general solution, that should be 
portable to all Schemes.

(4) To make things easily understandable, bindings defined in a source block 
that is included in another source block, should either always leak, or never. 
Not sometimes this way and sometimes that way. But conditionally wrapping with 
(let ...) if there is a :var header argument introduces a behavior that changes 
that.

(5) Imagine you already have a finished document and want to extend it, by 
letting one of the source blocks take a variable via :var header argument. Now 
you must change the source block as well, and not only place the variable's name 
somewhere, but also need to take care of imports and evaluation and expansion 
phases.

(6) Looking at other languages and how it behaves there. Say Python for example, 
there are no (let ...) expressions available, so no let-wrapping could happen. 
Unless org invents an additional `def` to wrap it in that, but only if there are 
:var header arguments. Lets see how things work there:

~~~~START~~~~
#+name: python-imports
#+begin_src python :python /usr/bin/python3 :results output replace drawer :var x=4
import math

y = math.sqrt(x)
# print(y)
#+end_src

#+name: python-usage
#+begin_src python :python /usr/bin/python3 :return :noweb strip-export :results value replace drawer
<<python-imports>>

print("y: {}".format(y))
#+end_src
~~~~~END~~~~~

Unfortunately, this example does not seem to work at all, but for a different 
reason:

It seems that using any Python source block with :var header args via :noweb 
does not work, as it then behaves in the way, that it merely pasted the included 
source block, without first putting in the :var values into the variables. I get 
errors about those :var variables being undefined, of course, since they are on 
the included source block, not on the including one:

~~~~START: *Org-Babel Error Output*~~~~
Traceback (most recent call last):
   File "<stdin>", line 10, in <module>
   File "<stdin>", line 5, in main
NameError: name 'x' is not defined
[ Babel evaluation exited with code 1 ]
~~~~~END~~~~~

So it would seem, that :var does not work with :noweb for Python source blocks yet.

(7) As an idea, one could still invent a new header argument that determines 
whether to let-wrap or not, if it is really needed for any other Scheme.

OK, to wrap up (ha!), I want to ask:

(q1) What is a rationale, if any, behind the let-wrapping?

(q2) Any chances of that changing to (define ...)?

(q3) How could I change my org-mode's code to not  let-wrap, and instead use 
(define ...)?

Best regards,
Zelphir

-- 
repositories:https://notabug.org/ZelphirKaltstahl

[-- Attachment #2: Type: text/html, Size: 9860 bytes --]

  reply	other threads:[~2023-03-09  0:45 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-07 11:27 org-babel guile source block bug in handling multiple values Zelphir Kaltstahl
2023-03-07 14:36 ` Ihor Radchenko
2023-03-07 15:18   ` Zelphir Kaltstahl
2023-03-07 19:52     ` Bruno Barbier
2023-03-08  0:55       ` Zelphir Kaltstahl
2023-03-08 19:38         ` Bruno Barbier
2023-03-09  0:44           ` Zelphir Kaltstahl [this message]
2023-03-09 13:04             ` [BUG] Inconsistent global/local :var assignments in ob-* for lisps and non-lisps (was: org-babel guile source block bug in handling multiple values) Ihor Radchenko
2023-03-10 10:39               ` Zelphir Kaltstahl
2023-03-11  9:58                 ` Ihor Radchenko
2023-03-11 18:30                   ` Zelphir Kaltstahl
2023-03-12 11:33                     ` Ihor Radchenko
2023-03-19 13:50                   ` [PATCH] lisp/ob-scheme.el Zelphir Kaltstahl
2023-03-22 10:43                     ` Ihor Radchenko
2023-03-25 14:34                       ` Zelphir Kaltstahl
2023-03-26  9:32                         ` Ihor Radchenko
2023-04-25 12:28                         ` Ihor Radchenko
2023-04-29 11:08                           ` Zelphir Kaltstahl
2023-03-09 13:10             ` org-babel guile source block bug in handling multiple values Ihor Radchenko
2023-03-10 10:42               ` Zelphir Kaltstahl
2023-03-11 10:18                 ` Ihor Radchenko
2023-06-02 13:11                   ` Ihor Radchenko
2023-03-09 13:11             ` Ihor Radchenko
2023-03-09 14:21               ` Daniel Kraus
2023-03-10 11:57                 ` Ihor Radchenko
2023-03-10 10:45               ` Zelphir Kaltstahl
2023-03-08  1:13       ` Zelphir Kaltstahl
2023-03-08  8:55         ` Ihor Radchenko
2023-03-07 15:44 ` Max Nikulin
2023-03-07 21:41 ` Rudolf Adamkovič

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c6a7b45c-cb80-8102-ac21-8ac8592c71a7@posteo.de \
    --to=zelphirkaltstahl@posteo.de \
    --cc=brubar.cs@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    --cc=yantar92@posteo.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).