On 3/8/23 20:38, Bruno Barbier wrote:
Hi Zelphir,
Zelphir Kaltstahl<zelphirkaltst...@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