Subject: bug#59786: Allowing arbitrary expressions in cl-labels

From: Stefan Monnier <[email-masked]>
Date: Tue, 29 Oct 2024 10:51:26 -0400

>> -forms of the function body.  FUNC is defined in any BODY, as well
>> +forms of the function body.  FUNC is defined in any BODY or EXP, as well
>                                                             ^^^^^^
> This is unfortunately not correct, e.g.
>
> (cl-labels ((even (if (odd 5)
>                       (lambda (x) (if (= x 0) t (odd (1- x))))
>                     #'ignore))
>             (odd  (lambda (x) (if (= x 0) nil (even (1- x))))))
>          (list (even 42) (odd 42)))
>
> ~~> funcall: Symbol's function definition is void: nil

This is normal.  Same happens with

    (letrec ((even (if (funcall odd 5)
                          (lambda (x) (if (= x 0) t (funcall odd (1- x))))
                        #'ignore))
             (odd  (lambda (x) (if (= x 0) nil (funcall even (1- x))))))
      (list (funcall even 42) (funcall odd 42)))

This construct is used for recursive functions, not recursive expressions.
Recursive expressions can't be handled without going for some kind of
lazy evaluation strategy which is quite difficult to pull off in a macro.

> Dunno how useful it is at all that the EXPs can use any of the defined
> functions.

As mentioned (admittedly, a long time ago), the purpose is to *compute*
the recursive functions, e.g.

    (cl-labels ((f1 (if (fboundp 'foo)
                        (lambda (x) ... (f1 ..) ...)
                      (lambda (x) ... (f1 ..) ...))))
      ...)

My original motivation is to define recursive oclosures (for PEG):

    (cl-labels ((f1 (oclosure-lambda ... (f1 ..) ...)))
      ...)

- Stefan