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

Reply via email to