Hi Ihor, thanks for following up.
Ihor Radchenko <[email protected]> writes: > Daniel Bausch <[email protected]> writes: > >>> Probably, it should. Consider a block with :eval no. If you want to run >>> it, there should be a way to override :eval. >> >> I realized myself, while playing around with the prototype >> implementation, that I actually quite often need to not only assign >> variables, but also need to override header arguments. I therefore >> developed this further to have a special separator key ":&headers" in >> the argument list after which header arguments can be specified. > > Introducing new semantics is generally not the best idea. > Ideally, we should reuse something either from Elisp, or, at least, from > Org. Agreed. If there's a cleaner way to achieve the same, I would appreciate this - always. I think I bent almost everything Org provides out of the box, before proposing this feature. I (ab)used :post, noweb, named one-liners, header arguments defined in properties, you name it. >> I have developed another machinery (cascading header arg classes - off >> topic here) that allows me to switch the target database much easier. >> So with "#+call:" I write. >> >> #+name: foo >> #+begin_src sql :var hello="world" >> SELECT '$hello' FROM dual; >> #+end_src >> >> #+call: foo("org") :class alice@db1 >> >> #+call: foo("mode") :class bob@db2 > > While it may be tempting to develop whole new concepts for Org, > especially when you can implement them with LLM, I strongly advice > trying built-in options and customizations first. Org (and Elisp) have > plenty of features that are flexible enough to fit many needs. Adding > new things will produce subtle (or not so subtle) bugs, sooner or later, > especially as you update to new Org version developed without your new > additions in mind. Org, especially Babel, has been my daily driver for more than 15 years now. That :class thing was born out of essential necessity. I couldn't do my work without it. I'm working with multiple databases and database users - often inside one Org node. Having to detail out all the required header arguments for the different database connections for every block would be unfeasable - let alone proper password handling. My configuration behind it transparently retrieves and caches the passwords, which are stored in an encrypted password store. Of course it might break with some update, but that would be a minor thing. Not having it would be worse. If you are interested, I can share the implementation. >> The equivalent with the :&headers key is then: >> >> #+begin_src elisp >> (let ((result1 (org-babel-call "foo")) >> (result2 (org-babel-call "foo" :hello "org" :&headers :class >> "alice@db1")) >> (result3 (org-babel-call "foo" :hello "mode" :&headers :class >> "bob@db2"))) >> ...) >> #+end_src > > What about simply > > (org-babel-call "foo" > :class "alice@db1" > :var hello "org") > > and making org-babel-call a macro (so that hello do not need to be > quoted)? Would it still compose as a macro? E.g., could I call one block assigning its variables using the result of two others? >> I also thought about adding support for positional arguments which >> "#+call" also has. (Those then do not look like header arguments >> anymore.) >> >> (org-babel-call "foo" "org") >> (org-babel-call "foo" "org" :&headers :class "alice@db1") > > This idea has merits, but I am not a fan of new concept with :&headers. I'm not a huge fan myself either. It was the cleanest thing that came to my mind though. That's how it would be done in bash probably. > Alternative idea: > > (org-babel-call (foo "org") > :class "alice@db1) > (org-babel-call (foo :hello "org") > :class "alice@db1" :eval "yes") > > (foo :hello "org") will follow normal Elisp function semantics but call > src blocks. Header args specified as extra. > > A bit like `eval'. Looks (!) very good at first sight, but does it compose? Does it work with lisp variables and all kinds of values given as arguments? ----- I have a companion proposal ready which we might keep in mind before deciding on a sytnax for org-babel-call. It's currently called as (org-babel-result "block-name"). It retrieves the current result from the named block similar to how it would be done if :cache yes were specified on that block. So it intentionally takes the current result from the buffer without recalculating a block. It can be used from within elisp blocks, of course, but also in a variable assignment inside a header argument, like: #+name: hello #+begin_src python print("hello world") #+end_src #+results: : hello world #+begin_src python :var hello=(org-babel-result "hello") print(hello) #+end_src Executing the second block does not execute the first block. It just takes the result and assigns it to hello for the second. It comes handy, when you (as always) have forgotton to put a :cache yes on a long running block and want to further process the result. As these two proposal complement each other, their syntax should be similar. (org-babel-call "foo") (org-babel-result "foo") These look quite complementary. (org-babel-result (foo)) however doesn't make sense. Your earlier/simpler idea would align much better and could be extended to positional and normal reference assignment as well, by just using every argument before the first keyword argument as a positional variable assigment from a lisp value and switching assigment modes depending on the number of non-keyword arguments after :var. (org-babel-call "foo" positional1 positional2 :dir "/home/daniel") (org-babel-call "bar" :var "var1" lisp-variable) ;; (1) (org-babel-call "xyz" :var "var2=ref[3]") ;; (2) (org-babel-call "zyx" (org-babel-result "query")) ;; (3) 1) Two non-keyword args after :var => assignment by value - I would rather not make the word after :var somehow magic 2) Only one non-keyword arg after :var => assigment using org-babel-ref-resolve as usual in #+call 3) (org-babel-result "...") looks and composes similarly. Kind regards, Daniel
